pax_global_header00006660000000000000000000000064147505026250014520gustar00rootroot0000000000000052 comment=fdf0d417bcefc6a82be7349d47bbef14e8d5fbbb selint-1.5.1/000077500000000000000000000000001475050262500130225ustar00rootroot00000000000000selint-1.5.1/.github/000077500000000000000000000000001475050262500143625ustar00rootroot00000000000000selint-1.5.1/.github/workflows/000077500000000000000000000000001475050262500164175ustar00rootroot00000000000000selint-1.5.1/.github/workflows/main.yml000066400000000000000000000112201475050262500200620ustar00rootroot00000000000000# Copyright 2020 Tresys Technology, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: C/C++ CI on: [push, pull_request] jobs: build: name: build (${{ matrix.cc }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: cc: [ 'gcc', 'clang' ] include: - is_release_job: false - is_release_job: true cc: 'gcc' env: CC: ${{ matrix.cc }} steps: - uses: actions/checkout@v4 # Prevent Clang from using DWARF5, not supported by the valgrind version in runners - name: Set environment for Clang run: | echo "CC=clang-19" >> $GITHUB_ENV echo "CFLAGS=-gdwarf-4 -O2" >> $GITHUB_ENV if: ${{ matrix.cc == 'clang' }} - name: install clang repo run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main' -y sudo apt-get update -q sudo apt-get install -y clang-19 if: ${{ matrix.cc == 'clang' }} - name: install dependencies run: | sudo apt-get update -q sudo apt-get install -y check uthash-dev libconfuse-dev autoconf-archive bison byacc flex valgrind help2man bats - name: autogen run: ./autogen.sh - name: configure run: | if [[ ${{ matrix.cc }} == 'gcc' ]] then ./configure --enable-werror CFLAGS=--coverage || (cat config.log; exit 1;) else ./configure --enable-werror || (cat config.log; exit 1;) fi - name: make run: make - name: make check run: make check || (cat tests/test-suite.log; exit 1;) - name: make check-valgrind run: make check-valgrind || (cat tests/test-suite-memcheck.log; exit 1;) - name: make distcheck run: make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror - name: functional tests run : | cd ./tests/functional BATS_TIME_EXPENSIVE=1 bats end-to-end.bats - name: Test distribution run : | make VERSION=pipeline-test dist tar xvzf selint-pipeline-test.tar.gz cd selint-pipeline-test ./configure --enable-werror make cd tests/functional BATS_TIME_EXPENSIVE=1 bats end-to-end.bats - name: checkout refpolicy uses: actions/checkout@v4 with: repository: SELinuxProject/refpolicy path: refpolicy - name: refpolicy parse test run : | cd refpolicy/ make conf ../src/selint -c ../selint.conf -s -r -e C-001 --summary-only . - name: Release Info id: release_info run: | if [[ '${{ github.ref }}' == refs/tags/v* && ${{ matrix.is_release_job }} == 'true' ]] then echo is_release=true >> $GITHUB_OUTPUT echo tag=$(echo ${{ github.ref }} | cut -dv -f2) >> $GITHUB_OUTPUT else echo is_release=false >> $GITHUB_OUTPUT fi shell: bash - name: Create Release id: create_release if: steps.release_info.outputs.is_release == 'true' uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: v${{ steps.release_info.outputs.tag }} draft: false prerelease: false - name: Upload Release Artifact if: steps.release_info.outputs.is_release == 'true' uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: # This pulls from the CREATE RELEASE step above, referencing it's ID to # get its outputs object, which include a `upload_url` upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ./selint-${{ steps.release_info.outputs.tag }}.tar.gz asset_name: selint-${{ steps.release_info.outputs.tag }}.tar.gz asset_content_type: application/tar+gzip whitespace_check: name: Whitespace Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: check-whitespaces run: git diff-tree --check origin/main HEAD selint-1.5.1/.gitignore000066400000000000000000000014731475050262500150170ustar00rootroot00000000000000selint *.o *.log *.trs *.gcda *.gcno parse.c parse.h lex.c Makefile Makefile.in config.* autoscan.log autom4te.cache configure *.swp aclocal.m4 tests/check_tree tests/check_parse_functions tests/check_parsing tests/check_maps tests/check_parse_fc tests/check_template tests/check_file_list tests/check_fc_checks tests/check_check_hooks tests/check_selint_config tests/check_if_checks tests/check_string_list tests/check_runner tests/check_startup tests/check_te_checks tests/check_ordering tests/check_perm_macro tests/check_name_list tests/functional/policies/parse_errors/test3_tmp.if tests/functional/policies/parse_errors/test5_tmp.te tests/functional/policies/parse_errors/test6_tmp.if tests/test-suite.log .deps depcomp compile install-sh missing test-driver ylwrap stamp-h1 man/selint.1 vgcore.* tests/vgcore.* *.tar.gz selint-1.5.1/.lgtm.yml000066400000000000000000000002321475050262500145630ustar00rootroot00000000000000extraction: cpp: prepare: packages: "autoconf-archive" configure: command: - ./autogen.sh - ./configure --without-check selint-1.5.1/CHANGELOG000066400000000000000000000127241475050262500142420ustar00rootroot00000000000000# SELint Changelog ## [1.5.1] 2025-02-04 ### Added (checks) - S-011 For file_context lines with only whitespace ### Fixed - No longer treat lines with only whitespace in fc files as an error (converted to style check S-011) - Support CIDR address notation in nodecon statements - Support m4 quoted strings in interface call arguments - Allow @ characters in quoted strings ## [1.5.0] 2024-01-09 ### Changed - Checks about requires now support userspace classperm requires - Various CI improvements - Clearer message for W-011 ### Fixed - Support quoted genfscon paths - Compile with gcc 14 ## [1.4.0] 2023-02-03 ### Added (checks) - W-013 for audit_access permissions in allow and auditallow rules ### Changed - Warn if duplicate configuration files are detected in a policy - selint-disable commands now work on tunable blocks ### Fixed - Support ifn?def blocks in .if files - Various bug fixes ## [1.3.0] 2022-09-17 ### Added (checks) - New check level: eXtra. Checks intended to be run on occasion for code inspection, not regularly - X-001 for unused interfaces - X-002 for av rule exclusions - C-008 for valid conditional identifiers ### Changed - Improve ordering to be more permissive in line with user expectations - Parser errors now display the full path to the unparsed file ### Fixed - Various parser fixes - Support anonymous inodes - Handle selint-disable correctly with require blocks - Avoid false positive W-002 warnings in the presence of S-004 - Various bug fixes ## [1.2.1] 2022-01-10 ### Changed - Checks referencing attributes now see attributes assigned to a type as part of a type declaration ### Fixed - Various parser fixes and enhancements - Support IVv4 address embedding ## [1.2.0] 2021-01-18 ### Added (general features) - New ./configure option --enable-werror to compile treating errors as warnings. - Parse errors now display info about exactly where in the line the failure occurred - spec file for building on rpm based distros ### Added (checks) - C-006 for unordered declarations in require blocks - C-007 for missing use of the self keyword - E-009 for empty optional and require blocks - E-010 for stray bare words ### Changed - New C-001 ordering option refpolicy-light (see sample selint.conf for details) - Extend S-009 to support multi-class av rules - Improved error messages for various error cases - selint-disable now allows a space before check ids - You can now use multiple --context arguments ### Fixed - Support object class specifications in role transitions - Lots of cleanup and fixes for C-001 ordering checks - Support extended permission class av rules (allowxperm etc) - Internal cleanup and improvements - Allow multiple roles in role allow statements ## [1.1.0] 2020-05-19 ### Added (general features) - -S flag to print a summary of issue found following an analysis - --context flag to specify additional files to parse but not scan. This is primarily helpful if you want to only scan your modified files in a full source repository. (If you are planning on loading your local modules into your already installed policy and have local development headers in a standard location, selint should find them by default and you don't need to use this option) - -F flag to return an error code on issues found - --summary-only flag to output a summary of issues found without displaying the actual issues - ./configure flag to disable unit testing. This can be used on systems with older versions of libcheck. - Colored output ### Added (checks) - C-005 for permission ordering - S-003 for unneeded semicolons - S-004 for template calls from interfaces - S-005 for declarations in an interface - S-006 for the use of a bare module statement instead of policy_module() macro - S-007 for the use of a gen_context() macro with no mls component specified - S-008 for gen_require() macro calls with unquoted arguments - S-009 for permission macros that don't match object class - S-010 suggestiong usage of permission macros - W-006 for interface call with empty arguments - W-007 for unexpected spaces in interface arguments - W-008 for permission lists with * or ~ - W-009 for module name not matching file name - E-006 for declaration/interface name clash - E-007 for usage of unknown permission macros ### Changed - Turn C-001 off by default. - Assume the presence of system_u user and object_r role if no config is loaded. - SELint will no longer scan file_contexts file that are probably generated by the build system. This behavior can be turned off by setting skip_checking_generated_fcs=false in the config - W-001 and W-002 check additional types of rules ### Fixed - Man page generation in distribution tarballs now works after make clean - documentation cleanup - Various parser fixes - Clean up of check C-001 ## [1.0.2] - 2020-01-30 ### Fixed - (Issue #11) Include CHANGELOG, LICENSE and testing input files in release tarballs ## [1.0.1] - 2020-01-28 ### Added - Warning for invalid check ids in config or on command line - CI for github pushes - Enabled many warnings in build system - Changelog ### Fixed - Check W-002 now handles multiple requires in the same interface correctly - (Issue #2) Handle types prefixed with "-" correctly in checks - Handle type aliases correctly in checks W-002 and W-003 - Fix double free in certain parse error corner cases - Look at role transitions in relevant checks (C-001, W-002, W-003) - Fix false positives on W-004 when a regex character is in square brackets - General Code Cleanup ## [1.0.0] - 2020-01-15 Initial Release selint-1.5.1/INSTALL000077500000000000000000000366141475050262500140700ustar00rootroot00000000000000Installation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell command './configure && make && make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the 'README' file for instructions specific to this package. Some packages provide this 'INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The 'configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a 'Makefile' in each directory of the package. It may also create one or more '.h' files containing system-dependent definitions. Finally, it creates a shell script 'config.status' that you can run in the future to recreate the current configuration, and a file 'config.log' containing compiler output (useful mainly for debugging 'configure'). It can also use an optional file (typically called 'config.cache' and enabled with '--cache-file=config.cache' or simply '-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how 'configure' could check whether to do them, and mail diffs or instructions to the address given in the 'README' so they can be considered for the next release. If you are using the cache, and at some point 'config.cache' contains results you don't want to keep, you may remove or edit it. The file 'configure.ac' (or 'configure.in') is used to create 'configure' by a program called 'autoconf'. You need 'configure.ac' if you want to change it or regenerate 'configure' using a newer version of 'autoconf'. The simplest way to compile this package is: 1. 'cd' to the directory containing the package's source code and type './configure' to configure the package for your system. Running 'configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type 'make' to compile the package. 3. Optionally, type 'make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type 'make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the 'make install' phase executed with root privileges. 5. Optionally, type 'make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior 'make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing 'make clean'. To also remove the files that 'configure' created (so you can compile the package for a different kind of computer), type 'make distclean'. There is also a 'make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type 'make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide 'make distcheck', which can by used by developers to test that all other targets like 'make install' and 'make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the 'configure' script does not know about. Run './configure --help' for details on some of the pertinent environment variables. You can give 'configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU 'make'. 'cd' to the directory where you want the object files and executables to go and run the 'configure' script. 'configure' automatically checks for the source code in the directory that 'configure' is in and in '..'. This is known as a "VPATH" build. With a non-GNU 'make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use 'make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple '-arch' options to the compiler but only a single '-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the 'lipo' tool if you have problems. Installation Names ================== By default, 'make install' installs the package's commands under '/usr/local/bin', include files under '/usr/local/include', etc. You can specify an installation prefix other than '/usr/local' by giving 'configure' the option '--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option '--exec-prefix=PREFIX' to 'configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like '--bindir=DIR' to specify different values for particular kinds of files. Run 'configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of '${prefix}', so that specifying just '--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to 'configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the 'make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, 'make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of '${prefix}'. Any directories that were specified during 'configure', but not in terms of '${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the 'DESTDIR' variable. For example, 'make install DESTDIR=/alternate/directory' will prepend '/alternate/directory' before all installation names. The approach of 'DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of '${prefix}' at 'configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving 'configure' the option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. Some packages pay attention to '--enable-FEATURE' options to 'configure', where FEATURE indicates an optional part of the package. They may also pay attention to '--with-PACKAGE' options, where PACKAGE is something like 'gnu-as' or 'x' (for the X Window System). The 'README' should mention any '--enable-' and '--with-' options that the package recognizes. For packages that use the X Window System, 'configure' can usually find the X include and library files automatically, but if it doesn't, you can use the 'configure' options '--x-includes=DIR' and '--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of 'make' will be. For these packages, running './configure --enable-silent-rules' sets the default to minimal output, which can be overridden with 'make V=1'; while running './configure --disable-silent-rules' sets the default to verbose, which can be overridden with 'make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX 'make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as 'configure' are involved. Use GNU 'make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its '' header file. The option '-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put '/usr/ucb' early in your 'PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in '/usr/bin'. So, if you need '/usr/ucb' in your 'PATH', put it _after_ '/usr/bin'. On Haiku, software installed for all users goes in '/boot/common', not '/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features 'configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, 'configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the '--build=TYPE' option. TYPE can either be a short name for the system type, such as 'sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file 'config.sub' for the possible values of each field. If 'config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option '--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with '--host=TYPE'. Sharing Defaults ================ If you want to set default values for 'configure' scripts to share, you can create a site shell script called 'config.site' that gives default values for variables like 'CC', 'cache_file', and 'prefix'. 'configure' looks for 'PREFIX/share/config.site' if it exists, then 'PREFIX/etc/config.site' if it exists. Or, you can set the 'CONFIG_SITE' environment variable to the location of the site script. A warning: not all 'configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to 'configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the 'configure' command line, using 'VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified 'gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash 'configure' Invocation ====================== 'configure' recognizes the following options to control how it operates. '--help' '-h' Print a summary of all of the options to 'configure', and exit. '--help=short' '--help=recursive' Print a summary of the options unique to this package's 'configure', and exit. The 'short' variant lists options used only in the top level, while the 'recursive' variant lists options also present in any nested packages. '--version' '-V' Print the version of Autoconf used to generate the 'configure' script, and exit. '--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally 'config.cache'. FILE defaults to '/dev/null' to disable caching. '--config-cache' '-C' Alias for '--cache-file=config.cache'. '--quiet' '--silent' '-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to '/dev/null' (any error messages will still be shown). '--srcdir=DIR' Look for the package's source code in directory DIR. Usually 'configure' can determine that directory automatically. '--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. '--no-create' '-n' Run the configure checks, but stop before creating any output files. 'configure' also accepts some other, not widely useful, options. Run 'configure --help' for more details. selint-1.5.1/LICENSE000066400000000000000000000261361475050262500140370ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. selint-1.5.1/Makefile.am000066400000000000000000000017061475050262500150620ustar00rootroot00000000000000# Copyright 2019 Tresys Technology, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. AUTOMAKE_OPTIONS = foreign if WITH_CHECK SUBDIRS = src . tests . man check-valgrind: CK_FORK=no $(MAKE) -C tests check-valgrind-memcheck-am else SUBDIRS = src . man check-local: @echo "SELint was configured without Check support!!" @exit 1 endif selintconfdir = $(sysconfdir) selintconf_DATA = selint.conf EXTRA_DIST = selint.conf CHANGELOG LICENSE NOTICE check_examples.txt selint-1.5.1/NOTICE000066400000000000000000000000551475050262500137260ustar00rootroot00000000000000SELint Copyright 2019 Tresys Technology, LLC selint-1.5.1/README.md000066400000000000000000000173621475050262500143120ustar00rootroot00000000000000# Summary SELint is a program to perform static code analysis on SELinux policy source files. ## Installing from tar download To install from a downloaded tarball, first install the following dependencies: On rpm based distros: * uthash-devel * libconfuse * libconfuse-devel * check * check-devel On apt based distros: * uthash-dev * libconfuse-dev * check Then run: ``` ./configure make make install ``` ## Installing from git If you are building from a git repo checkout, you'll also need bison, flex, autotools (automake, autoconf, aclocal, autoreconf) and the autoconf-archive package. Then you can run `./autogen.sh` to set up autotools and then follow the steps above. ## Usage ``` selint [OPTIONS] FILE [...] ``` ### Options ``` -c CONFIGFILE, --config=CONFIGFILE Override default config with config specified on command line. See CONFIGURATION section for config file syntax. --color=COLOR_OPTION Configure color output. Options are on, off and auto (the default). --context=CONTEXT_PATH Also parse any .te or .if files found in CONTEXT_PATH and load symbols associated with them for use when checking the policy files to be analyzed. No checks are run on these files. Implies -s. --debug-parser Enable debug output for the internal policy parser. Very noisy, useful to debug parsing failures. -d CHECKID, --disable=CHECKID Disable check with the given ID. -e CHECKID, --enable=CHECKID Enable check with the given ID. -E, --only-enabled Only run checks that are explicitly enabled with the --enable option. -F, --fail Exit with a non-zero value if any issue was found. -h, --help Show help menu about command line options. -l LEVEL, --level=LEVEL Only list errors with a severity level at or greater than LEVEL. Options are C (convention), S (style), W (warning), E (error), F (fatal error). See SEVERITY LEVELS for more information. If this option is not specified, SELint will default to the level selected in the applicable config file. --scan-hidden-dirs Scan hidden directories. By default hidden directories (like `.git`) are skipped in recursive mode. -s, --source Run in "source mode" to scan a policy source repository that is designed to compile into a full system policy. If this flag is not specified, SELint will assume that scanned policy files are intended to be loaded into the currently running system policy. -S, --summary Display a summary of issues found after running the analysis. --summary-only Only display a summary of issues found after running the analysis. Do not show the individual findings. Implies -S. -r, --recursive Scan recursively and check all SELinux policy files found. -v, --verbose Enable verbose output -V, --version Show version information and exit. ``` ### Configuration A global configuration is specified at the install prefix supplied to `./configure` (typically `/usr/local/etc`). This can be overridden on the command line using the -c option. Options specified on the command line override options from the config file. See the global config file for details on config file syntax. ### Severity levels SELint messages are assocatied with a severity level, indicating the significance of the issue. Available levels are listed below in increasing order of significance. * X (extra) - Miscellaneous checks, mainly for policy introspection. These must be explicitly enabled with their individual identifier. * C (convention) - A violation of common style conventions * S (style) - Stylistic "code smell" that may be associated with unintended behavior * W (warning) - Non standard policy that may result in issues such as run time errors or security issues * E (error) - Important issues that may result in errors at compile time or run time * F (fatal error) - Error that prevents further processing ### SELint exceptions To eliminate one or more checks on one line, add a comment containing a string in any of the following formats: * `selint-disable:E-003` * `selint-disable: E-003` * `selint-disable:E-003,E-004` * `selint-disable: E-003, E-004` This is currently only supported in te and if files ### Output SELint outputs messages in the following format: ``` [filename]:[lineno]: ([SEVERITY LEVEL]): [MESSAGE] ([ISSUE ID]) ``` For example: ``` example.te:127: (E) Interface from module not in optional_policy block (E-001) ``` ### Check IDs The following checks may be performed: Extra Checks: * X-001: Unused interface or template declaration * X-002: AV rule with excluded source or target (can affect policy binary size) Convention Checks: * C-001: Violation of refpolicy te file ordering conventions * C-004: Interface does not have documentation comment * C-005: Permissions in av rule or class declaration not ordered * C-006: Declarations in require block not ordered * C-007: Redundant type specification instead of self keyword * C-008: Conditional expression identifier from foreign module Style Checks: * S-001: Require block used instead of interface call * S-002: File context file labels with type not declared in module * S-003: Unnecessary semicolon * S-004: Template call from an interface * S-005: Declaration in interface * S-006: Bare module statement * S-007: Call to gen_context omits mls component * S-008: Unquoted gen_require block * S-009: Permission macro suffix does not match class name * S-010: Permission macro usage suggested * S-011: File context line containing only white spaces Warning Checks: * W-001: Type, attribute or userspace class referenced without explicit declaration * W-002: Type, attribute, role or userspace class used but not listed in require block in interface * W-003: Unused type, attribute, role or userspace class listed in require block * W-004: Potentially unescaped regex character in file contexts paths * W-005: Interface call from module not in optional_policy block * W-006: Interface call with empty argument * W-007: Unquoted space in argument of interface call * W-008: Allow rule with complement or wildcard permission * W-009: Module name does not match file name * W-010: Call to unknown interface * W-011: Declaration in require block not defined in own module * W-012: Conditional expression contains unknown identifier * W-013: Incorrect usage of audit_access permission Error Checks: * E-002: Bad file context format * E-003: Nonexistent user listed in fc file * E-004: Nonexistent role listed in fc file * E-005: Nonexistent type listed in fc file * E-006: Declaration and interface with same name * E-007: Usage of unknown permission or permission macro * E-008: Usage of unknown class * E-009: Empty optional or require macro block * E-010: Usage of unknown simple m4 macro or stray word Fatal Error Checks: * F-001: Policy syntax error prevents further processing * F-002: Internal error in SELint ## Reference policy conventions To improve the accuracy and avoid false-positives SELint makes some assumptions about naming conventions and formatting of the policy: * Type identifiers should end with the suffix `_t`. * Role identifiers should end with the suffix `_r`. * Names of noop interfaces for availability checks should end with the suffix `_stub`. * Permission macros should end with the suffix `_perms`. * Class set macros should end with the suffix `_class_set`. * Security class declarations of userspace classes in the security_classes file should be declared with a comment including the word `userspace`. * Interfaces that wrap a file based type-transition should end with the suffix `_filetrans`. * Interfaces that transforms their arguments, e.g. associate an attribute with them, and thus should be handled like a declaration should have one of the following common suffixes: `_type`, `_file`, `_domain`, `_node`, `_agent`, `_delivery`, `_sender`, `_boolean`, `_content`, `_constrained`, `_executable`, `_exemption`, `_object` or `_mountpoint`. selint-1.5.1/autogen.sh000077500000000000000000000011741475050262500150260ustar00rootroot00000000000000#!/bin/sh # Copyright 2019 Tresys Technology, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. autoreconf -fiv -Wall -Wno-portability selint-1.5.1/check_examples.txt000066400000000000000000000047061475050262500165450ustar00rootroot00000000000000 Here are examples of errors that will cause each possible result: Extra: X-001: # generates av rules for each type assigned to the attribute 'domain' # except 'foo_t', which might be thousands allow { domain -foo_t } self:process signal; Convention: C-001: type foo_t; allow foo_t self:process signal; type bar_t; C-004: interface(`foo_read_conf',` ... ') C-005: allow foo_t bar_t:file { write read }; C-006: gen_require(` type foo_t, bar_t; class foobar; ') C-007: allow myapp_t myapp_t:process signal; Style: S-001: require { type foo_t; } S-003: foo(bar); S-004: template(`bar',`') interface(`foo',` bar() ') S-005: interface(`foo',` type foo_t; ') S-006: module foo 1.0; S-007: gen_context(system_u:object_r:foo_t) S-008: gen_require( type foo_t; ) S-009: allow foo_t bar_t:file read_fifo_file_perms; S-010: allow foo_t bar_t:file { open read }; S-011: # file context line containing only white spaces Warning: W-001: # etc_t is defined in files module read_files_pattern(foo_t, etc_t, etc_t) W-002: interface(`foo_read_conf',` read_files_pattern($1, foo_conf_t, foo_conf_t) ') W-003: interface(`foo_read_conf',` gen_require(` type foo_conf_t; ') ') W-004: /path/with/unescaped.dot -- gen_context(system_u:object_r:foo_exec_t,s0) W-005: # foo is set to module in modules.conf foo_read_conf() W-006: foo() W-007: foo(bar baz) W-008: allow foo_t bar_t:file *; W-009: #In foo.te policy_module(bar, 1.0) W-010: init_this-if-does-not-exist(bar_t) W-011: interface(`foo_read_conf',` gen_require(` type this_type_does_not_exist; # type from different module type bar_conf_t; ') ') W-012: # in module foo if (bar_cond) { ... } W-013: allow foo_t bar_t:file audit_access; Error: E-002: /usr/bin/foo -- gen_erquire(system_u:object_r:foo_exec_t, s0) E-003: /usr/bin/foo -- gen_require(not_a_valid_user:object_r:foo_exec_t, s0) E-004: /usr/bin/foo -- gen_require(system_u:not_a_valid_role:foo_exec_t, s0) E-004: /usr/bin/foo -- gen_require(system_u:object_r:not_a_valid_type, s0) E-005: # There is no type named foo_exce_t defined in our policy /usr/bin/foo -- gen_require(system_u:object_r:foo_exce_t, s0) E-006: attribute foo; interface(`foo',`') E-007: allow foo_t bar_t:file reed_file_perms; E-008: allow foo_t bar_t:File read_file_perms; E-009: optional_policy(` #do_something(type_t) ') E-010: bare_m4_macro selint-1.5.1/configure.ac000066400000000000000000000056471475050262500153240ustar00rootroot00000000000000# Copyright 2019 Tresys Technology, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) AC_INIT([selint], [1.5.1], [Daniel.Burgener@microsoft.com]) AC_CONFIG_SRCDIR([src]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([subdir-objects]) # Checks for programs. AC_PROG_CC AC_PROG_CC_STDC AC_PROG_LEX AC_PROG_YACC # Check for testsuite Check library AC_ARG_WITH([check], [AS_HELP_STRING([--without-check], [Build without testsuite depending on Check (default: Build with testsuite)])], [with_check=${withval}], [with_check=yes]) AM_CONDITIONAL([WITH_CHECK], [test "x$with_check" = "xyes"]) AS_IF([test "x$with_check" = "xyes"], [PKG_CHECK_MODULES([CHECK], [check >= 0.11.0], [], [AC_MSG_ERROR([Check not found])])]) # Checks for libraries. AC_SEARCH_LIBS([cfg_init], [confuse], [], [ AC_MSG_ERROR([Unable to find libconfuse]) ]) # Checks for header files. AC_FUNC_ALLOCA AC_CHECK_HEADERS([inttypes.h libintl.h malloc.h stddef.h stdlib.h string.h unistd.h stdbool.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT8_T AC_TYPE_SIZE_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT8_T # Checks for library functions. AC_CHECK_FUNCS([memset strdup]) AC_ARG_ENABLE([gcov], [AS_HELP_STRING([--enable-gcov], [use Gcov to test the test suite])], [enable_gcov=${enableval}], []) AM_CONDITIONAL([COND_GCOV],[ test "x$enable_gcov" "=" "xyes" ]) AC_CHECK_PROGS([HELP2MAN], [help2man]) if ! test -z "$HELP2MAN" then AC_SUBST(MANPAGES, selint.1) HAVE_HELP2MAN=true fi AM_CONDITIONAL([HAVE_HELP2MAN], [test "x$HAVE_HELP2MAN" "=" "xtrue"]) AX_VALGRIND_DFLT([drd], [off]) AX_VALGRIND_DFLT([helgrind], [off]) AX_VALGRIND_DFLT([sgcheck], [off]) AX_VALGRIND_CHECK AC_CHECK_HEADER([uthash.h], [], [AC_MSG_ERROR([Unable to find uthash header])]) AM_CFLAGS="-Wall -Wextra -Wcast-qual -Wconversion -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wpointer-arith -Wshadow -Wstrict-prototypes -Wundef -Wunused -Wwrite-strings" AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat compiler warnings as errors (default: Do not treat as errors)])], [AM_CFLAGS+=" -Werror"]) AC_SUBST([AM_CFLAGS]) AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile man/Makefile]) AC_OUTPUT() selint-1.5.1/contributing.md000066400000000000000000000062501475050262500160560ustar00rootroot00000000000000# Contributing to SELint Thanks for your interest in contributing! You can find helpful tips and guidelines for contributing below. ## How Can I Contribute? ### Reporting Bugs See something wrong with SELint? Please let us know by filing a bug in the Issue Tracker. Before creating a bug, check the issue tracker to see if your issue has already been reported. When reporting the issue, please provide enough information to allow developers to reproduce the problem, including simple policy snippets that reveal the issue where possible. Don't forget to include your distro, SELint version, command line flags you ran SELint with, and any relevant configuration. ### Suggesting enhancements Feel free to submit requests for new features or additional policy checks. If submitting a check, please provide an example of compliant and non-compliant policy on that issue. ## Contributing Code ### Installing the latest source Make sure you're working against the latest source by checking out github master. In order to build the full project with autotools, you'll need to install the autoconf-archive package and then run ./autogen.sh. Then you can follow the instructions in the README for building from a release tarball. ### Submitting your code Please submit contributions via github PR. This is set up to run automated tests against all changes. If submitting code doing multiple different things, please break PRs up so each PR contains one logical change. (For example, if you were adding a new check and also found and fixed a bug with another check along the way, please submit separate PRs for each). ### Getting Started If you're looking for a place to start learning the code base, I recommend either fixing a bug from the issue tracker (check for the "good first issue" label for easy places to start), or adding a new check. When fixing a bug, please let a maintainer know that you're working on it so we can assign it to you and other developers know not to duplicate effort. If it takes you a while, that's fine, but please provide regular updates in the ticket, so we know you're still working and haven't abandoned it. ### Adding checks One of SELint's design goals is to make it straightforward for new developers to add checks. The first thing to do is to decide where to put your check. Checks live in te\_checks.{c,h}, if\_checks.{c,h}, and fc\_checks.{c,h}. While your check will apply to all three kinds of files where applicable, this provides some level of logical organization. If your check is about typical policy rules that can occur in both .te files or inside interfaces, put it in te\_checks; if it is about something specific to interfaces or file contexts, put it in if\_checks or fc\_checks respectively. Your check should have the following prototype: `struct check_result *check_function(const struct check_data * data, const struct policy_node * node);` In order to allow your check to be run, you need to add it to the register\_checks() function in runner.h, where you specify the type of node you should be run on, as well as the Check ID number, and your check function. Please also include unit tests for your check and update the README and check\_examples.txt selint-1.5.1/man/000077500000000000000000000000001475050262500135755ustar00rootroot00000000000000selint-1.5.1/man/Makefile.am000066400000000000000000000015761475050262500156420ustar00rootroot00000000000000# Copyright 2019 Tresys Technology, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. SOURCES=selint.h2m if HAVE_HELP2MAN man1_MANS=$(MANPAGES) EXTRA_DIST=$(man1_MANS) $(SOURCES) CLEANFILES=$(MANPAGES) selint.1: ../src/main.c $(SOURCES) $(HELP2MAN) -n "Perform static source code analysis on SELinux policy source files" -N -i selint.h2m -o $@ ../src/selint else EXTRA_DIST=$(SOURCES) endif selint-1.5.1/man/selint.h2m000066400000000000000000000002121475050262500154760ustar00rootroot00000000000000[AUTHOR] Daniel Burgener [REPORTING_BUGS] Report bugs at https://github.com/SELinuxProject/selint/issues selint-1.5.1/selint.conf000066400000000000000000000070051475050262500151710ustar00rootroot00000000000000# This is the configuration file for SELint. The global configuration file # is the default if no other configuration is provided, but can be overridden # by user specific and project specific configurations, or a configuration file # can be provided on the command line. # Set the severity level to report. Options are "convention", "style", # "warning", "error" and "fatal". SELint will report all errors at or above # the selected level severity = "convention" # Check enabling works as follows each step may override the one prior: # 1. All checks at or above the set severity level are enabled by default # 2. Checks may be disabled using the "disable" option below # 3. Checks may be enabled in either normal or source modes using the "enable_normal" and "enable_source" options below # 4. Checks may be disabled on the command line using the -d option # 5. Checks may be enabled on the command line using the -e option # Uncomment and modify to disable selected checks. This can be overridden on # the command line disable = { C-001, C-006, W-010, W-011, E-003, E-004 } # enable description #enable_normal = { S-002, E-002 } enable_source = { W-010, W-011, E-003, E-004 } # Modules.conf location. If you are running SELint in "source mode", you need # to supply a modules.conf file in order to run all checks. SELint will look # in the default location of policy/modules.conf from the directory where it # is run. To use a different path specify it here. #modules_conf_path = policy/modules.conf # Users and roles are often not declared in te files like other policy # constructs. Use the below options to specify valid users and roles that # SELint should not report as invalid even if they are not found in the # policy assume_users = { system_u } assume_roles = { object_r } # If you have defined any custom macros for use in fc files, list them here, # otherwise, SELint will report E-002 on occurrences of them # custom_fc_macros = { } # If you have defined any custom simple macros for use in te and if files, list them here, # otherwise, SELint will report E-010 on occurrences of them # custom_te_simple_macros = { } # What ordering standard you would like to apply when running check C-001 # Options are: # - refpolicy: Follow the refpolicy Style Guide (https://github.com/SELinuxProject/refpolicy/wiki/StyleGuide) # - refpolicy-light: Similar to refpolicy, but do not distinct base interface calls (except kernel module) # - refpolicy-lax: (default) Similar to refpolicy, but ignore interface # and block ordering requirements. ordering_rules = "refpolicy-lax" # What ordering in require blocks you would like to apply when running check C-006 # The following six flavors must each be used exactly once: # attribute, attribute_role, bool, class, role, type # If unset, this defaults to the following order: #ordering_requires = { bool, role, attribute_role, attribute, type, class } ordering_requires = { bool, attribute, attribute_role, type, class, role } # Whether to check the order (alphabetically) of requires of the same flavor when running check C-006 # If unset, this defaults to true. ordering_requires_same_flavor = false # Whether to ignore known false-positives on generated policy files. # In recursive mode SELint checks all files with known endings, regardless # if they are source or build generated files, like base.fc . # Currently the following checks are going to be disabled on build generated # files: # S-002: base.fc, all_mods.fc, $MODNAME.mod.fc # If unset, this defaults to true. #skip_checking_generated_fcs = true selint-1.5.1/selint.spec000066400000000000000000000021541475050262500151760ustar00rootroot00000000000000Summary: SELinux policy source file checker Name: selint Version: 1.1.0 Release: 1%{?dist} URL: https://github.com/SELinuxProject/selint License: ASL 2.0 %global forgeurl https://github.com/SELinuxProject/selint %global branch master # handle not having forge macros on el7 %{!?forgesoure: %define forgesource %forgeurl/archive/%branch/selint-%branch.tar.gz} %{!?forgesetup: %define forgesetup %setup -n %name-%branch} %{!?forgemeta: %define forgemeta %nil} %forgemeta Source0: %{forgesource} BuildRequires: autoconf autoconf-archive automake bison check check-devel flex gcc help2man libconfuse libconfuse-devel uthash-devel # pkgconfig Requires: libconfuse %if 0%{?fedora} || 0%{?rhel} >= 8 Requires: check %endif %description SELint is a program to perform static code analysis on SELinux policy source files %prep %forgesetup %build [[ -x ./configure ]] || ./autogen.sh %if 0%{?rhel} == 7 %{configure} --without-check %else %{configure} %endif %{make_build} %install %{make_install} %files %license LICENSE %doc CHANGELOG README %{_bindir}/selint %config(noreplace) %{_sysconfdir}/selint.conf %{_mandir}/man1/selint.1.gz selint-1.5.1/src/000077500000000000000000000000001475050262500136115ustar00rootroot00000000000000selint-1.5.1/src/Makefile.am000066400000000000000000000026341475050262500156520ustar00rootroot00000000000000# Copyright 2019 Tresys Technology, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. bin_PROGRAMS = selint selint_SOURCES = main.c lex.l parse.y tree.c tree.h selint_error.h parse_functions.c parse_functions.h maps.c maps.h runner.c runner.h parse_fc.c parse_fc.h template.c template.h file_list.c file_list.h check_hooks.c check_hooks.h fc_checks.c fc_checks.h util.c util.h if_checks.c if_checks.h selint_config.c selint_config.h string_list.c string_list.h startup.c startup.h te_checks.c te_checks.h ordering.c ordering.h color.c color.h perm_macro.c perm_macro.h xalloc.h name_list.c name_list.h BUILT_SOURCES = parse.h AM_YFLAGS = -d -Wno-other -Wno-yacc -Werror=conflicts-rr -Werror=conflicts-sr if COND_GCOV MAYBE_COVERAGE=--coverage -fno-inline -fno-inline-small-functions -fno-default-inline endif AM_CFLAGS += $(MAYBE_COVERAGE) -DSYSCONFDIR='"$(sysconfdir)"' MOSTLYCLEANFILES = *.gcda *.gcno *.gcov lex.c parse.c parse.h selint-1.5.1/src/check_hooks.c000066400000000000000000000172771475050262500162530ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE #include #include #include #include #include "check_hooks.h" #include "color.h" #include "xalloc.h" int found_issue = 0; int suppress_output = 0; int full_path = 0; enum selint_error add_check(enum node_flavor check_flavor, struct checks *ck, const char *check_id, struct check_result *(*check_function)(const struct check_data *check_data, const struct policy_node *node)) { struct check_node *loc; if (ck->check_nodes[check_flavor]) { loc = ck->check_nodes[check_flavor]; while (loc->next) { loc = loc->next; } loc->next = xmalloc(sizeof(struct check_node)); loc = loc->next; } else { ck->check_nodes[check_flavor] = xmalloc(sizeof(struct check_node)); loc = ck->check_nodes[check_flavor]; } loc->check_function = check_function; loc->check_id = xstrdup(check_id); loc->issues_found = 0; loc->next = NULL; return SELINT_SUCCESS; } enum selint_error call_checks(struct checks *ck, const struct check_data *data, const struct policy_node *node) { return call_checks_for_node_type(ck->check_nodes[node->flavor], data, node); } enum selint_error call_checks_for_node_type(struct check_node *ck_list, const struct check_data *data, const struct policy_node *node) { struct check_node *cur = ck_list; while (cur) { if (node->exceptions && strstr(node->exceptions, cur->check_id)) { cur = cur->next; continue; } struct check_result *res = cur->check_function(data, node); if (res) { found_issue = 1; cur->issues_found++; res->lineno = node->lineno; if (!suppress_output) { display_check_result(res, data); } free_check_result(res); } cur = cur->next; } return SELINT_SUCCESS; } void display_check_result(const struct check_result *res, const struct check_data *data) { static const size_t FILENAME_PADDING = 22; const char *name; unsigned int padding; if (full_path) { name = data->filepath; padding = 0; } else { name = data->filename; const size_t len = strlen(name); if (FILENAME_PADDING < len) { padding = 0; } else { padding = (unsigned)(FILENAME_PADDING - len); } } printf("%s:%*u: %s(%c)%s: %s (%c-%03u)\n", name, padding, res->lineno, color_severity(res->severity), res->severity, color_reset(), res->message, res->severity, res->check_id); } struct check_result *alloc_internal_error(const char *string) { return make_check_result('F', F_ID_INTERNAL, "%s", string); } bool is_valid_severity(char check_char) { switch (check_char) { case 'C': case 'S': case 'W': case 'E': case 'F': return true; } return false; } int is_valid_check(const char *check_str) { if (!check_str) { return 0; } if (check_str[1] != '-') { return 0; } int max_id = 0; char severity = check_str[0]; switch (severity) { case 'X': max_id = X_END - 1; break; case 'C': max_id = C_END - 1; break; case 'S': max_id = S_END - 1; break; case 'W': max_id = W_END - 1; break; case 'E': max_id = E_END - 1; break; case 'F': max_id = 2; break; default: return 0; } int check_id = atoi(check_str+2); if (check_id > 0 && check_id <= max_id) { return 1; } else { return 0; } } // Return the number of check nodes in the checks structure static unsigned int count_check_nodes(const struct checks *ck) { unsigned int count = 0; for (int i=0; i <= NODE_ERROR; i++) { if (ck->check_nodes[i]) { struct check_node *cur = ck->check_nodes[i]; while (cur) { count++; cur = cur->next; } } } return count; } static int id_priority(char id) { switch (id) { case 'X': return 0; case 'C': return 1; case 'S': return 2; case 'W': return 3; case 'E': return 4; default: return 5; //Should never happen, but no way to return an error } } // Return negative if n1 goes before n2, positive if n1 goes after n2 or equal if they are equivalent static int comp_check_nodes(const void *n1, const void *n2) { const struct check_node *node1 = *(const struct check_node *const *)n1; const struct check_node *node2 = *(const struct check_node *const *)n2; int check1_priority = id_priority(node1->check_id[0]); int check2_priority = id_priority(node2->check_id[0]); int r = (check1_priority > check2_priority) - (check1_priority < check2_priority); if (r != 0) { return r; } int node1_id = atoi(node1->check_id + 2); int node2_id = atoi(node2->check_id + 2); return (node1_id > node2_id) - (node1_id < node2_id); } void display_check_issue_counts(const struct checks *ck) { size_t num_nodes = count_check_nodes(ck); unsigned int printed_something = 0; // Build flat array of check nodes struct check_node **node_arr = xcalloc(num_nodes, sizeof(struct check_node *)); unsigned int node_arr_index = 0; for (int i=0; i <= NODE_ERROR; i++) { if (ck->check_nodes[i]) { struct check_node *cur = ck->check_nodes[i]; while (cur) { node_arr[node_arr_index] = cur; cur = cur->next; node_arr_index++; } } } qsort((void *) node_arr, num_nodes, sizeof(struct check_node *), comp_check_nodes); unsigned int issue_count = 0; const char *old_issue_name = NULL; for (unsigned int i=0; i < num_nodes; i++) { if (old_issue_name && 0 != strcmp(old_issue_name, node_arr[i]->check_id)) { // New issue. Print the old info if (issue_count != 0) { printf("%s%s%s: %u\n", color_severity(old_issue_name[0]), old_issue_name, color_reset(), issue_count); printed_something = 1; } // Start counting new issue_count = node_arr[i]->issues_found; } else { // Same issue as previous element issue_count += node_arr[i]->issues_found; } old_issue_name = node_arr[i]->check_id; } // Possible print last issue if (issue_count != 0) { printf("%s%s%s: %u\n", color_severity(old_issue_name[0]), old_issue_name, color_reset(), issue_count); printed_something = 1; } if (!printed_something) { printf("%s(none)%s\n", color_ok(), color_reset()); } free(node_arr); } void free_check_result(struct check_result *res) { if (res) { free(res->message); } free(res); } struct check_result *make_check_result(char severity, unsigned int check_id, const char *format, ...) { struct check_result *res = xmalloc(sizeof(struct check_result)); res->severity = severity; res->check_id = check_id; va_list args; va_start(args, format); if (vasprintf(&res->message, format, args) == -1) { res->severity = 'F'; res->check_id = F_ID_INTERNAL; res->message = xstrdup("Failed to generate check result message"); } va_end(args); return res; } void free_checks(struct checks *to_free) { if (to_free == NULL) { return; } for (int i=0; i < NODE_ERROR + 1; i++) { free_check_node(to_free->check_nodes[i]); } free(to_free); } void free_check_node(struct check_node *to_free) { while (to_free) { struct check_node *tmp = to_free; to_free = to_free->next; free(tmp->check_id); free(tmp); } } selint-1.5.1/src/check_hooks.h000066400000000000000000000163061475050262500162500ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CHECK_HOOKS_H #define CHECK_HOOKS_H #include "tree.h" #include "selint_error.h" #include "selint_config.h" enum extra_ids { X_ID_UNUSED_IF = 1, X_ID_EXCL_AV = 2, X_END }; enum convention_ids { C_ID_TE_ORDER = 1, C_ID_IF_COMMENT = 4, C_ID_UNORDERED_PERM = 5, C_ID_UNORDERED_REQ = 6, C_ID_SELF = 7, C_ID_FOREIGN_CONDID = 8, C_END }; enum style_ids { S_ID_REQUIRE = 1, S_ID_FC_TYPE = 2, S_ID_SEMICOLON = 3, S_ID_IF_CALLS_TEMPL = 4, S_ID_DECL_IN_IF = 5, S_ID_BARE_MODULE = 6, S_ID_MISSING_RANGE = 7, S_ID_UNQUOTE_GENREQ = 8, S_ID_PERM_SUFFIX = 9, S_ID_PERMMACRO = 10, S_ID_FC_SPACES = 11, S_END }; enum warn_ids { W_ID_NO_EXPLICIT_DECL = 1, W_ID_NO_REQ = 2, W_ID_UNUSED_REQ = 3, W_ID_FC_REGEX = 4, W_ID_IF_CALL_OPTIONAL = 5, W_ID_EMPTY_IF_CALL_ARG = 6, W_ID_SPACE_IF_CALL_ARG = 7, W_ID_RISKY_ALLOW_PERM = 8, W_ID_MOD_NAME_FILE = 9, W_ID_UNKNOWN_CALL = 10, W_ID_IF_DECL_NOT_OWN = 11, W_ID_UNKNOWN_COND_ID = 12, W_ID_AUDIT_ACCESS = 13, W_END }; enum error_ids { E_ID_FC_ERROR = 2, E_ID_FC_USER = 3, E_ID_FC_ROLE = 4, E_ID_FC_TYPE = 5, E_ID_DECL_IF_CLASH = 6, E_ID_UNKNOWN_PERM = 7, E_ID_UNKNOWN_CLASS = 8, E_ID_EMPTY_BLOCK = 9, E_ID_STRAY_WORD = 10, E_END }; enum fatal_ids { F_ID_POLICY_SYNTAX = 1, F_ID_INTERNAL = 2 }; enum file_flavor { FILE_TE_FILE, FILE_IF_FILE, FILE_FC_FILE }; struct check_data { char *mod_name; const char *filepath; char *filename; enum file_flavor flavor; const struct config_check_data *config_check_data; }; // A check is responsible for filling out all fields except lineno // which is filled out by the calling function.` struct check_result { unsigned int lineno; char severity; unsigned int check_id; char *message; }; struct check_node { struct check_result *(*check_function) (const struct check_data * data, const struct policy_node * node); char *check_id; unsigned int issues_found; struct check_node *next; }; struct checks { struct check_node *check_nodes[NODE_ERROR + 1]; }; // Whether an issue was found extern int found_issue; // Whether found issues are printed individually extern int suppress_output; // Whether to print full paths extern int full_path; /********************************************* * Add an check to be called on check_flavor nodes * check_flavor - The flavor of node to call the check for * ck - The check structure to add the check to * check_id - The ID code for the check * check_function - the check to add * returns SELINT_SUCCESS or an error code on failure *********************************************/ enum selint_error add_check(enum node_flavor check_flavor, struct checks *ck, const char *check_id, struct check_result *(*check_function)(const struct check_data * check_data, const struct policy_node * node)); /********************************************* * Call all registered checks for node->flavor node types * and write any error messages to STDOUT * ck - The checks structure * data - Metadata about the file * node - the node to check * returns SELINT_SUCCESS or an error code on failure *********************************************/ enum selint_error call_checks(struct checks *ck, const struct check_data *data, const struct policy_node *node); /********************************************* * Helper function for call_checks that takes the appropriate * list of checks for the node flavor and writes any error messages to STDOUT * ck_list - The checks to run * data - Metadata about the file * node - the node to check * returns SELINT_SUCCESS or an error code on failure *********************************************/ enum selint_error call_checks_for_node_type(struct check_node *ck_list, const struct check_data *data, const struct policy_node *node); /********************************************* * Display a result message for a positive check finding * res - Information about the result of the check * data - Metadata about the file *********************************************/ void display_check_result(const struct check_result *res, const struct check_data *data); /********************************************* * Creates a check_result, using a printf style format string and optional * arguments to generate a message * severity - The severity of the check result * check_id - The check identifier * format - A printf style format string *********************************************/ __attribute__ ((format(printf, 3, 4))) struct check_result *make_check_result(char severity, unsigned int check_id, const char *format, ...); /********************************************* * Generates a check result for an internal error (F-002) * string - The error message to display *********************************************/ struct check_result *alloc_internal_error(const char *string); /********************************************* * Determine if a character represents a valid severity. * check_char - The character to check * returns true if it is a valid check and false otherwise *********************************************/ bool is_valid_severity(char check_char); /********************************************* * Determine if a string represents a valid check. * This compares vs all checks that are defined in the ids enums * check_str - The string to check * returns 1 if it is a valid check and 0 otherwise *********************************************/ int is_valid_check(const char *check_str); /********************************************* * Display a count of issues found in a run, but check ID. * Don't display checks with no issues found * ck - The checks structure, which should be already populated with issues_found * from an analysis run *********************************************/ void display_check_issue_counts(const struct checks *ck); void free_check_result(struct check_result *); void free_checks(struct checks *to_free); void free_check_node(struct check_node *to_free); #endif selint-1.5.1/src/color.c000066400000000000000000000033121475050262500150720ustar00rootroot00000000000000/* * Copyright 2020 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "color.h" static int enabled = 0; #define COLOR_RESET "\033[0m" #define COLOR_BOLD "\033[1m" #define COLOR_RED "\033[31;1m" #define COLOR_GREEN "\033[32;1m" #define COLOR_YELLOW "\033[33;1m" #define COLOR_BLUE "\033[34;1m" #define COLOR_MAGENTA "\033[35;1m" #define COLOR_CYAN "\033[36;1m" #define EMPTY_STR "" void color_enable(void) { enabled = 1; } const char *color_reset(void) { if (!enabled) { return EMPTY_STR; } return COLOR_RESET; } const char *color_error(void) { if (!enabled) { return EMPTY_STR; } return COLOR_RED; } const char *color_warning(void) { if (!enabled) { return EMPTY_STR; } return COLOR_YELLOW; } const char *color_note(void) { if (!enabled) { return EMPTY_STR; } return COLOR_MAGENTA; } const char *color_ok(void) { if (!enabled) { return EMPTY_STR; } return COLOR_GREEN; } const char *color_severity(char severity) { if (!enabled) { return EMPTY_STR; } switch (severity) { case 'E': return COLOR_RED; case 'W': return COLOR_YELLOW; case 'S': return COLOR_MAGENTA; case 'C': return COLOR_BLUE; } return COLOR_BOLD; } selint-1.5.1/src/color.h000066400000000000000000000041061475050262500151010ustar00rootroot00000000000000/* * Copyright 2020 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef COLOR_H #define COLOR_H /********************************************* * Enable colored output. * All color functions only return color control sequences, if colored output has been enabled. * Prior to that all color functions return an empty string. *********************************************/ void color_enable(void); /********************************************* * Reset any previous color setting. * Should be used after any marking color function. *********************************************/ const char *color_reset(void); /********************************************* * Mark the following output with an error color. *********************************************/ const char *color_error(void); /********************************************* * Mark the following output with a warning color. *********************************************/ const char *color_warning(void); /********************************************* * Mark the following output with a note color. *********************************************/ const char *color_note(void); /********************************************* * Mark the following output with an ok color. *********************************************/ const char *color_ok(void); /********************************************* * Mark the following output with the appropriate color for the given severity. * severity - The severity to decide the color. *********************************************/ const char *color_severity(char severity); #endif /* COLOR_H */ selint-1.5.1/src/fc_checks.c000066400000000000000000000171371475050262500156760ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "color.h" #include "fc_checks.h" #include "maps.h" #include "tree.h" #include "util.h" #define SETUP_FOR_FC_CHECK(node) \ if (node->flavor != NODE_FC_ENTRY) { \ return alloc_internal_error("File context type check called on non file context entry"); \ } \ const struct fc_entry *entry = node->data.fc_data; \ if (!entry) { \ return alloc_internal_error("Policy node data field is NULL"); \ } \ if (!entry->context) { \ return NULL; \ } \ struct check_result *check_file_context_types_in_mod(const struct check_data *data, const struct policy_node *node) { SETUP_FOR_FC_CHECK(node) if (data->config_check_data->skip_checking_generated_fcs) { // do not check probably generated base and entire filecontext file // do not check probably generated module filecontext files static bool notified = false; if (0 == strcmp("base.fc", data->filename) || 0 == strcmp("all_mods.fc", data->filename) || ends_with(data->filename, strlen(data->filename), ".mod.fc", strlen(".mod.fc"))) { if (!notified) { printf("%sNote%s: Check S-002 is not performed against generated filecontext files (e.g. %s).\n"\ " This can be disabled with the configuration setting \"skip_checking_generated_fcs\".\n", color_note(), color_reset(), data->filename); notified = true; } return NULL; } } const char *type_decl_mod_name = look_up_in_decl_map(entry->context->type, DECL_TYPE); if (!type_decl_mod_name) { // If the type is not in any module, that's a different error // Returning success on an error condition may seem weird, but it is a // redundant condition with another check that will catch this if enabled. // Enabling this check and disabling the undeclared check is a valid // (although strange) configuration which will result in this condition not // being logged, but that is what the user has specifically requested in that // situation. The more common case is having both checks on, and there we // don't want to double log return NULL; } if (strcmp(data->mod_name, type_decl_mod_name)) { return make_check_result('S', S_ID_FC_TYPE, "Type %s is declared in module %s, but used in file context here.", entry->context->type, type_decl_mod_name); } return NULL; } struct check_result *check_gen_context_no_range(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { SETUP_FOR_FC_CHECK(node) if (entry->context->has_gen_context && !entry->context->range) { return make_check_result('S', S_ID_MISSING_RANGE, "No mls levels specified in gen_context"); } return NULL; } struct check_result *check_file_context_regex(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { SETUP_FOR_FC_CHECK(node) const char *path = entry->path; char cur = *path; char prev = '\0'; int error = 0; while (cur != '\0') { char next = *(path + 1); if (cur == '[' && prev != '\\') { // Fast forward through [ ] groups, because regex characters // should not be escaped there while (cur != '\0' && (cur != ']' || prev == '\\')) { next = *(path + 1); prev = cur; cur = next; path++; } continue; } switch (cur) { case '.': // require that periods are either escaped or are one of ".*", ".+", or ".?" // rarely are periods actually used to just mean one of any character if (prev != '\\' && next != '*' && next != '+' && next != '?') { error = 1; } break; case '+': case '*': // require that pluses and asterisks are either escaped or look // something kindof like ".*", "(...)*", or "[...]*" if (prev != '\\' && prev != '.' && prev != ']' && prev != ')') { error = 1; } break; default: break; } if (error) { return make_check_result('W', W_ID_FC_REGEX, "File context path contains a potentially unescaped regex character '%c' at position %d: %s", cur, (int)(path - entry->path + 1), entry->path); } prev = cur; cur = next; path++; } return NULL; } struct check_result *check_file_context_error_nodes(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->flavor == NODE_ERROR) { return make_check_result('E', E_ID_FC_ERROR, "Bad file context format"); } if (node->flavor == NODE_EMPTY) { return make_check_result('S', S_ID_FC_SPACES, "File context line containing only white spaces"); } return alloc_internal_error("Invalid node type for `check_file_context_error_nodes`"); } struct check_result *check_file_context_users(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { SETUP_FOR_FC_CHECK(node) const char *user_decl_filename = look_up_in_decl_map(entry->context->user, DECL_USER); if (!user_decl_filename) { return make_check_result('E', E_ID_FC_USER, "Nonexistent user (%s) listed in fc_entry", entry->context->user); } return NULL; } struct check_result *check_file_context_roles(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { SETUP_FOR_FC_CHECK(node) const char *role_decl_filename = look_up_in_decl_map(entry->context->role, DECL_ROLE); if (!role_decl_filename) { return make_check_result('E', E_ID_FC_ROLE, "Nonexistent role (%s) listed in fc_entry", entry->context->role); } return NULL; } struct check_result *check_file_context_types_exist(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { SETUP_FOR_FC_CHECK(node) const char *type_decl_filename = look_up_in_decl_map(entry->context->type, DECL_TYPE); if (!type_decl_filename) { return make_check_result('E', E_ID_FC_TYPE, "Nonexistent type (%s) listed in fc_entry", entry->context->type); } return NULL; } selint-1.5.1/src/fc_checks.h000066400000000000000000000100031475050262500156640ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FC_CHECKS_H #define FC_CHECKS_H #include "check_hooks.h" /********************************************* * Check for issues with file context labels type field. * Called on NODE_FC_ENTRY nodes. * node - the node to check * returns NULL if passed or check_result for issue S-002 *********************************************/ struct check_result *check_file_context_types_in_mod(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for gen_context calls that omit an mls component * Called on NODE_FC_ENTRY nodes. * node - the node to check * returns NULL if passed or check_result for issue S-007 *********************************************/ struct check_result *check_gen_context_no_range(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for potentially unescaped regex characters. * Called on NODE_FC_ENTRY nodes; * node - the node to check * returns NULL if called on a node type other than error node * or a check_result for issue W-004 *********************************************/ struct check_result *check_file_context_regex(const struct check_data *data, const struct policy_node *node); /********************************************* * Report an error on error nodes in a file_context file * node - the node to check * returns NULL if called on a node type other than error node * or a check_result for issue E-002 *********************************************/ struct check_result *check_file_context_error_nodes(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for issues with file context labels user field * node - the node to check * returns NULL if passed or check_result for issue E-003 *********************************************/ struct check_result *check_file_context_users(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for issues with file context labels role field. * Called on NODE_FC_ENTRY nodes. * node - the node to check * returns NULL if passed or check_result for issue E-004 *********************************************/ struct check_result *check_file_context_roles(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for issues with file context labels type field. * Called on NODE_FC_ENTRY nodes. * node - the node to check * returns NULL if passed or check_result for issue E-005 *********************************************/ struct check_result *check_file_context_types_exist(const struct check_data *data, const struct policy_node *node); #endif selint-1.5.1/src/file_list.c000066400000000000000000000034531475050262500157340ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "file_list.h" #include "xalloc.h" void file_list_push_back(struct policy_file_list *list, struct policy_file *file) { if (list->tail) { list->tail->next = xmalloc(sizeof(struct policy_file_node)); list->tail = list->tail->next; } else { list->head = list->tail = xmalloc(sizeof(struct policy_file_node)); } list->tail->file = file; list->tail->next = NULL; } struct policy_file *make_policy_file(const char *filename, struct policy_node *ast) { struct policy_file *ret = xmalloc(sizeof(struct policy_file)); ret->filename = xstrdup(filename); ret->ast = ast; return ret; } int file_name_in_file_list(const char *filename, const struct policy_file_list *list) { const struct policy_file_node *node = list->head; while (node) { if (node->file && 0 == strcmp(filename, node->file->filename)) { return 1; } node = node->next; } return 0; } void free_file_list(struct policy_file_list *to_free) { struct policy_file_node *cur = to_free->head; while (cur) { free(cur->file->filename); free_policy_node(cur->file->ast); free(cur->file); struct policy_file_node *tmp = cur; cur = cur->next; free(tmp); } free(to_free); } selint-1.5.1/src/file_list.h000066400000000000000000000024551475050262500157420ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FILE_LIST_H #define FILE_LIST_H #include "tree.h" struct policy_file { char *filename; struct policy_node *ast; }; struct policy_file_node { struct policy_file *file; struct policy_file_node *next; }; struct policy_file_list { struct policy_file_node *head; struct policy_file_node *tail; }; void file_list_push_back(struct policy_file_list *list, struct policy_file *file); struct policy_file *make_policy_file(const char *filename, struct policy_node *ast); // Return 1 if filename matches the name of a file in list, and 0 otherwise int file_name_in_file_list(const char *filename, const struct policy_file_list *list); void free_file_list(struct policy_file_list *to_free); #endif selint-1.5.1/src/if_checks.c000066400000000000000000000361611475050262500157020ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "if_checks.h" #include "tree.h" #include "maps.h" #include "util.h" #define NOT_REQ_MESSAGE "%s %s is used in interface but not required" struct check_result *check_unused_interface(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->flavor != NODE_INTERFACE_DEF && node->flavor != NODE_TEMP_DEF) { return alloc_internal_error( "Unused interface check called on non interface definition entry"); } const char *if_name = node->data.str; if (is_used_if(if_name)) { return NULL; } return make_check_result('X', X_ID_UNUSED_IF, "Interface %s is unused", if_name); } struct check_result *check_interface_definitions_have_comment(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->flavor != NODE_INTERFACE_DEF && node->flavor != NODE_TEMP_DEF) { return alloc_internal_error( "Interface comment check called on non interface definition entry"); } if (!(node->prev) || node->prev->flavor != NODE_COMMENT) { return make_check_result('C', C_ID_IF_COMMENT, "No comment before interface definition for %s", node->data.str); } else { return NULL; } } static int compare_declaration_flavors(enum decl_flavor a, enum decl_flavor b, const struct config_check_data *config) { if (a == b) { return 0; } for (unsigned short i = 0; i < (sizeof config->order_requires / sizeof *config->order_requires); ++i) { if (a == config->order_requires[i]) { return -1; } if (b == config->order_requires[i]) { return 1; } } // should never happen return 0; } static int compare_declarations(const struct declaration_data *a, const struct declaration_data *b, const struct config_check_data *config) { int r = compare_declaration_flavors(a->flavor, b->flavor, config); if (r != 0) { return r; } if (!config->ordering_requires_same_flavor) { // ordering names of the same flavor is disabled in the config file return -1; } // ignore _t suffix, e.g. sort ssh_t before ssh_exec_t const char *a_ptr = a->name; const char *b_ptr = b->name; while (*a_ptr && *b_ptr) { if ((unsigned char)*a_ptr != (unsigned char)*b_ptr) { break; } ++a_ptr; ++b_ptr; } if (*a_ptr == 't' && !*(a_ptr + 1) && a_ptr != a->name && *(a_ptr - 1) == '_') { --a_ptr; } if (*b_ptr == 't' && !*(b_ptr + 1) && b_ptr != b->name && *(b_ptr - 1) == '_') { --b_ptr; } return (unsigned char)*a_ptr - (unsigned char)*b_ptr; } struct check_result *check_unordered_declaration_in_require(const struct check_data *data, const struct policy_node *node) { if (node->flavor != NODE_REQUIRE && node->flavor != NODE_GEN_REQ) { return alloc_internal_error( "Unordered declaration in require check called on non require node"); } const struct policy_node *child = node->first_child; if (!child || child->flavor != NODE_START_BLOCK) { return alloc_internal_error( "No start-block node in require block"); } child = child->next; if (!child) { return make_check_result('C', C_ID_UNORDERED_REQ, "Empty require block"); } const struct declaration_data *prev_decl_data = NULL; for (const struct policy_node *cur = child; cur; cur = cur->next) { if (cur->flavor != NODE_DECL) { return alloc_internal_error( "Non declaration node in require block"); } const struct declaration_data *decl_data = cur->data.d_data; if (prev_decl_data) { const int compare = compare_declarations(prev_decl_data, decl_data, data->config_check_data); if (compare > 0) { return make_check_result('C', C_ID_UNORDERED_REQ, "Unordered declaration in require block (%s %s before %s %s)", decl_flavor_to_string(prev_decl_data->flavor), prev_decl_data->name, decl_flavor_to_string(decl_data->flavor), decl_data->name); } if (compare == 0) { return make_check_result('C', C_ID_UNORDERED_REQ, "Repeated declaration in require block (%s %s)", decl_flavor_to_string(decl_data->flavor), decl_data->name); } } prev_decl_data = decl_data; } return NULL; } struct check_result *check_if_calls_template(const struct check_data *data, const struct policy_node *node) { if (data->flavor != FILE_IF_FILE) { return NULL; } const struct policy_node *parent = node->parent; while (parent && (parent->flavor != NODE_INTERFACE_DEF && parent->flavor != NODE_TEMP_DEF)) { parent = parent->parent; } if (!parent) { return NULL; } const char *call_name = node->data.ic_data->name; if (parent->flavor == NODE_INTERFACE_DEF && look_up_in_template_map(call_name)) { return make_check_result('S', S_ID_IF_CALLS_TEMPL, "interface %s calls template %s", parent->data.str, call_name); } return NULL; } struct check_result *check_decl_in_if(const struct check_data *data, const struct policy_node *node) { if (data->flavor != FILE_IF_FILE) { return NULL; } const struct policy_node *parent = node->parent; while (parent && (parent->flavor != NODE_INTERFACE_DEF && parent->flavor != NODE_TEMP_DEF)) { // ignore declarations in require blocks if (parent->flavor == NODE_GEN_REQ || parent->flavor == NODE_REQUIRE) { return NULL; } parent = parent->parent; } // only check interfaces if (!parent || parent->flavor != NODE_INTERFACE_DEF) { return NULL; } return make_check_result('S', S_ID_DECL_IN_IF, "Declaration of %s in interface", node->data.d_data->name); } struct check_result *check_unquoted_gen_require_block(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->data.gr_data->unquoted) { return make_check_result('S', S_ID_UNQUOTE_GENREQ, "Gen require block unquoted"); } return NULL; } struct check_result *check_name_used_but_not_required_in_if(const struct check_data *data, const struct policy_node *node) { if (data->flavor != FILE_IF_FILE) { return NULL; } const struct policy_node *cur = node; struct name_list *names_in_current_node = get_names_in_node(node); if (!names_in_current_node) { return NULL; } while (cur) { if (cur->flavor == NODE_INTERFACE_DEF || cur->flavor == NODE_TEMP_DEF) { break; } cur = cur->parent; } if (!cur) { free_name_list(names_in_current_node); return NULL; } // In a template or interface, and cur is a pointer to the definition node cur = cur->first_child; struct name_list *names_required = NULL; struct name_list *names_required_tail = NULL; while (cur && cur != node) { if (cur->flavor == NODE_GEN_REQ || cur->flavor == NODE_REQUIRE) { if (!names_required) { names_required = get_names_required(cur); names_required_tail = names_required; } else { names_required_tail->next = get_names_required(cur); } while (names_required_tail && names_required_tail->next) { names_required_tail = names_required_tail->next; } } cur = dfs_next(cur); // The normal case is that the gen_require block // is at the top level, but it could be nested, // for example in an ifdef } const struct name_list *name_node = names_in_current_node; /* In declarations skip the first name, which is the new declared type */ if (node->flavor == NODE_DECL) { name_node = name_node->next; } const char *flavor = NULL; while (name_node) { const struct name_data *ndata = name_node->data; if (!name_list_contains_name(names_required, ndata)) { if (name_is_role(ndata) && 0 == strcmp(ndata->name, "system_r")) { // system_r is required by default in all modules // so that is an exception that shouldn't be warned // about. name_node = name_node->next; continue; } if (name_is_type(ndata) && look_up_in_decl_map(ndata->name, DECL_TYPE)) { flavor = "Type"; } else if (name_is_typeattr(ndata) && look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE)) { flavor = "Attribute"; } else if (name_is_roleattr(ndata) && look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE_ROLE)) { flavor = "Role Attribute"; } else if (name_is_role(ndata) && look_up_in_decl_map(ndata->name, DECL_ROLE)) { flavor = "Role"; } else if (name_is_class(ndata) && look_up_in_decl_map(ndata->name, DECL_CLASS) && userspace_class_support && is_userspace_class(ndata->name, ndata->traits)) { flavor = "Class"; } else { // This is a string we don't recognize. Other checks and/or // the compiler catch invalid bare words name_node = name_node->next; continue; } struct check_result *res = make_check_result('W', W_ID_NO_REQ, NOT_REQ_MESSAGE, flavor, ndata->name); free_name_list(names_in_current_node); free_name_list(names_required); return res; } name_node = name_node->next; } free_name_list(names_in_current_node); free_name_list(names_required); return NULL; } struct check_result *check_name_required_but_not_used_in_if(const struct check_data *data, const struct policy_node *node) { if (data->flavor != FILE_IF_FILE) { return NULL; } struct declaration_data *dd = node->data.d_data; const char *flavor = ""; if (dd->flavor == DECL_TYPE) { flavor = "Type"; } else if (dd->flavor == DECL_ATTRIBUTE) { flavor = "Attribute"; } else if (dd->flavor == DECL_ATTRIBUTE_ROLE) { flavor = "Role Attribute"; } else if (dd->flavor == DECL_ROLE) { flavor = "Role"; } else if (dd->flavor == DECL_CLASS && userspace_class_support) { flavor = "Class"; if (!is_userspace_class(dd->name, dd->attrs)) { return make_check_result('W', W_ID_UNUSED_REQ, "Class %s is listed in require block but is not a userspace class", dd->name); } } else { return NULL; } const struct policy_node *cur = node; const struct policy_node *req_block_node = NULL; while (cur->parent && cur->flavor != NODE_INTERFACE_DEF && cur->flavor != NODE_TEMP_DEF) { if (cur->flavor == NODE_GEN_REQ || cur->flavor == NODE_REQUIRE) { req_block_node = cur; } cur = cur->parent; } if ((cur->flavor != NODE_INTERFACE_DEF && cur->flavor != NODE_TEMP_DEF) || !req_block_node) { // This check only applies to nodes in require blocks in interfaces return NULL; } // ignore interfaces with the ending '_stub'; used in Refpolicy as optional block decider if (cur->flavor == NODE_INTERFACE_DEF && ends_with(cur->data.str, strlen(cur->data.str), "_stub", strlen("_stub"))) { return NULL; } struct name_list *names_to_check = get_names_in_node(node); if (!names_to_check) { // This should never happen return alloc_internal_error( "Declaration with no declared items"); } cur = req_block_node; cur = cur->next; struct name_list *nl_end = NULL; struct name_list *nl_head = NULL; int depth = 0; while (cur) { struct name_list *names_used = get_names_in_node(cur); if (names_used) { if (!nl_head) { nl_head = nl_end = names_used; } else { nl_end->next = names_used; } while (nl_end->next) { nl_end = nl_end->next; } } if (cur->first_child) { cur = cur->first_child; depth++; } else if (cur->next) { cur = cur->next; } else { while (cur->parent && depth > 0) { cur = cur->parent; depth--; if (cur->next) { break; } } cur = cur->next; } } struct check_result *res = NULL; for (const struct name_list *name_node = names_to_check; name_node; name_node = name_node->next) { if (!name_list_contains_name(nl_head, name_node->data)) { res = make_check_result('W', W_ID_UNUSED_REQ, "%s %s is listed in require block but not used in interface", flavor, name_node->data->name); break; } } free_name_list(nl_head); free_name_list(names_to_check); return res; } struct check_result *check_required_declaration_own(const struct check_data *data, const struct policy_node *node) { if (data->flavor != FILE_IF_FILE) { return NULL; } const char *name = node->data.d_data->name; const enum decl_flavor flavor = node->data.d_data->flavor; // ignore class, permission and user declarations if (flavor == DECL_CLASS || flavor == DECL_PERM || flavor == DECL_USER) { return NULL; } // TODO: handle templated declarations if (name[0] == '$') { return NULL; } // only check declarations in require blocks if (!is_in_require(node)) { return NULL; } const char *modname_orig_decl = look_up_in_decl_map(name, flavor); if (!modname_orig_decl) { return make_check_result('W', W_ID_IF_DECL_NOT_OWN, "Definition of required %s %s not found in any module", decl_flavor_to_string(flavor), name); } if (0 == strcmp(modname_orig_decl, data->mod_name)) { return NULL; } // ignore roles declared in kernel module: common in refpolicy if (flavor == DECL_ROLE && 0 == strcmp(modname_orig_decl, "kernel")) { return NULL; } return make_check_result('W', W_ID_IF_DECL_NOT_OWN, "Definition of required %s %s not found in this interface's module, but in module %s", decl_flavor_to_string(flavor), name, modname_orig_decl); } selint-1.5.1/src/if_checks.h000066400000000000000000000140341475050262500157020ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef IF_CHECKS_H #define IF_CHECKS_H #include "check_hooks.h" /********************************************* * Check for unused interfaces and templates * Called on NODE_INTERFACE_DEF and NODE_TEMP_DEF nodes. * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue X-001 *********************************************/ struct check_result *check_unused_interface(const struct check_data *data, const struct policy_node *node); /********************************************* * Check to make sure all interfaces and templates have a comment above them * Called on NODE_INTERFACE_DEF and NODE_TEMP_DEF nodes. * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue C-004 *********************************************/ struct check_result *check_interface_definitions_have_comment(const struct check_data *data, const struct policy_node *node); /********************************************* * Check that declaration in require blocks are ordered * Called on NODE_REQUIRE and NODE_GEN_REQ nodes. * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue C-006 *********************************************/ struct check_result *check_unordered_declaration_in_require(const struct check_data *data, const struct policy_node *node); /********************************************* * Check that interfaces do not call templates * Called on NODE_IF_CALL nodes * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue S-004 *********************************************/ struct check_result *check_if_calls_template(const struct check_data *data, const struct policy_node *node); /********************************************* * Check that interfaces do contain declarations. * Called on NODE_DECL nodes. * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue S-005 *********************************************/ struct check_result *check_decl_in_if(const struct check_data *data, const struct policy_node *node); /********************************************* * Check that gen_require blocks are quoted * Called on NODE_GEN_REQ nodes * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue S-008 *********************************************/ struct check_result *check_unquoted_gen_require_block(const struct check_data *data, const struct policy_node *node); /********************************************* * Check that all names referenced in interface are listed in its require block * (or declared in that template) * Called on NODE_AV_RULE, NODE_TT_RULE and NODE_IF_CALL nodes. * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue W-002 *********************************************/ struct check_result *check_name_used_but_not_required_in_if(const struct check_data *data, const struct policy_node *node); /********************************************* * Check that all types listed in require block are actually used in the interface * Called on NODE_DECL nodes * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue W-003 *********************************************/ struct check_result *check_name_required_but_not_used_in_if(const struct check_data *data, const struct policy_node *node); /********************************************* * Check that all types listed in require block are declared in the same module * Called on NODE_DECL nodes * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue W-011 *********************************************/ struct check_result *check_required_declaration_own(const struct check_data *data, const struct policy_node *node); #endif selint-1.5.1/src/lex.l000066400000000000000000000173421475050262500145650ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ %{ #include #include #include "tree.h" #include "parse.h" #include "xalloc.h" extern void reset_current_lines(void); /* * Callback by the lexer, called prior to every matched rule's action. * Update the source file location accordingly. */ #define YY_USER_ACTION \ yylloc->first_line = yylloc->last_line = yylineno; \ yylloc->first_column = yycolumn; \ yylloc->last_column = yycolumn + yyleng - 1; \ yycolumn += yyleng; // used by parser char* current_lines[LINES_TO_CACHE] = { NULL }; unsigned line_cache_index = 0; // internal state for cached lines static size_t current_lines_alloc[LINES_TO_CACHE] = { 0 }; static size_t current_line_sent = 0; static size_t current_line_len = 0; void reset_current_lines(void) { for (unsigned i = 0; i < LINES_TO_CACHE; ++i) { free(current_lines[i]); current_lines[i] = NULL; current_lines_alloc[i] = 0; } line_cache_index = 0; current_line_sent = current_line_len = 0; } /* * Override the input method of the lexer. * Read a complete line and save it for error printing. * Must be a macro to access yyin. */ #undef YY_INPUT #define YY_INPUT(buf, result, max_size) \ size_t _avail = current_line_len - current_line_sent; \ if (_avail == 0) { \ current_line_sent = 0; \ line_cache_index = (line_cache_index + 1) % LINES_TO_CACHE; \ const ssize_t _res = getline(¤t_lines[line_cache_index], \ ¤t_lines_alloc[line_cache_index], \ yyin); \ if (_res < 0) { \ if (ferror(yyin)) { \ YY_FATAL_ERROR("Error reading input"); \ } \ _avail = 0; \ current_lines[line_cache_index][0] = '\0'; \ } else { \ _avail = (size_t)_res; \ } \ current_line_len = _avail; \ } \ if (_avail > (size_t)(max_size)) { \ _avail = (size_t)(max_size); \ } \ memcpy((buf), current_lines[line_cache_index] + current_line_sent, _avail); \ current_line_sent += _avail; \ (result) = (int)_avail; %} %option nounput %option noinput %option noyywrap %option nodefault %option yylineno %option reentrant %option bison-bridge %option bison-locations %option noyyalloc noyyfree noyyrealloc %% policy_module { return POLICY_MODULE; } module { return MODULE; } type { return TYPE; } typealias { return TYPEALIAS; } alias { return ALIAS; } attribute { return ATTRIBUTE; } bool { return BOOL; } typeattribute { return TYPE_ATTRIBUTE; } roleattribute { return ROLE_ATTRIBUTE; } role { return ROLE; } types { return TYPES; } attribute_role { return ATTRIBUTE_ROLE; } allow { return ALLOW; } allowxperm { return ALLOW_XPERM; } auditallow { return AUDIT_ALLOW; } auditallowxperm { return AUDIT_ALLOW_XPERM; } dontaudit { return DONT_AUDIT; } dontauditxperm { return DONT_AUDIT_XPERM; } neverallow { return NEVER_ALLOW; } neverallowxperm { return NEVER_ALLOW_XPERM; } type_transition { return TYPE_TRANSITION; } type_member { return TYPE_MEMBER; } type_change { return TYPE_CHANGE; } range_transition { return RANGE_TRANSITION; } role_transition { return ROLE_TRANSITION; } optional_policy { return OPTIONAL_POLICY; } gen_require { return GEN_REQUIRE; } gen_bool { return GEN_BOOL; } gen_tunable { return GEN_TUNABLE; } require { return REQUIRE; } tunable_policy { return TUNABLE_POLICY; } ifelse { return IFELSE; } refpolicywarn { return REFPOLICYWARN; } class { return CLASS; } common { return COMMON; } inherits { return INHERITS; } if { return IF; } else { return ELSE; } ifdef { return IFDEF; } ifndef { return IFNDEF; } genfscon { return GENFSCON; } sid { return SID; } portcon { return PORTCON; } netifcon { return NETIFCON; } nodecon { return NODECON; } fs_use_trans { return FS_USE_TRANS; } fs_use_xattr { return FS_USE_XATTR; } fs_use_task { return FS_USE_TASK; } define { return DEFINE; } gen_user { return GEN_USER; } gen_context { return GEN_CONTEXT; } permissive { return PERMISSIVE; } typebounds { return TYPEBOUNDS; } interface { return INTERFACE; } template { return TEMPLATE; } userdebug_or_eng { return USERDEBUG_OR_ENG; } [0-9]+\.[0-9]+(\.[0-9]+)? { yylval->string = xstrdup(yytext); return VERSION_NO; } [0-9]+ { yylval->string = xstrdup(yytext); return NUMBER; } [a-zA-Z\$\/][a-zA-Z0-9_\$\*\/\-]* { yylval->string = xstrdup(yytext); return STRING; } [0-9a-zA-Z\$\/][a-zA-Z0-9_\$\*\/\-]* { yylval->string = xstrdup(yytext); return NUM_STRING; } [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} { yylval->string = xstrdup(yytext); return IPV4; } [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2} { yylval->string = xstrdup(yytext); return IPV4_CIDR; } ([0-9A-Fa-f]{1,4})?\:([0-9A-Fa-f\:])*\:([0-9A-Fa-f]{1,4})?(\:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})? { yylval->string = xstrdup(yytext); return IPV6; } ([0-9A-Fa-f]{1,4})?\:([0-9A-Fa-f\:])*\:([0-9A-Fa-f]{1,4})?(\:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})?\/[0-9]{1,3} { yylval->string = xstrdup(yytext); return IPV6_CIDR; } \"[a-zA-Z0-9_\.\-\:~\$\[\]\/@]*\" { yylval->string = xstrdup(yytext); return QUOTED_STRING; } \-[\-ldbcsp][ \t] { return FILE_TYPE_SPECIFIER; } \( { return OPEN_PAREN; } \) { return CLOSE_PAREN; } \, { return COMMA; } \. { return PERIOD; } \{ { return OPEN_CURLY; } \} { return CLOSE_CURLY; } \: { return COLON; } \; { return SEMICOLON; } \` { return BACKTICK; } \' { return SINGLE_QUOTE; } \~ { return TILDE; } \* { return STAR; } \- { return DASH; } \&\& { return AND; } \|\| { return OR; } \^ { return XOR; } \!\= { return NOT_EQUAL; } \! { return NOT; } \=\= { return EQUAL; } \#selint\-disable\:\ ?[CSWEF]\-[0-9]+(\,\ ?[CSWEF]\-[0-9]+)*$ { yylval->string = xstrdup(yytext); return SELINT_COMMAND; } \#.*$ { return COMMENT; } dnl(.*)?$ ; /* skip m4 comment lines */ [ \t\n\r] ; /* normally skip whitespace */ . { yylval->symbol = *yytext; return UNKNOWN_TOKEN; } %% void *yyalloc(size_t bytes, __attribute__((unused)) void *yyscanner) { return xmalloc(bytes); } void *yyrealloc(void *ptr, size_t bytes, __attribute__((unused)) void *yyscanner) { return xrealloc(ptr, bytes); } void yyfree(void *ptr, __attribute__((unused)) void *yyscanner) { return free(ptr); } selint-1.5.1/src/main.c000066400000000000000000000555161475050262500147150ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "runner.h" #include "parse.h" #include "config.h" #include "file_list.h" #include "util.h" #include "selint_config.h" #include "startup.h" #include "color.h" #include "xalloc.h" // ASCII characters go up to 127 #define CONTEXT_ID 128 #define COLOR_ID 129 #define SUMMARY_ONLY_ID 130 #define SCAN_HIDDEN_DIRS_ID 131 #define DEBUG_PARSER_ID 132 #define FULL_PATH_ID 133 extern int yydebug; extern int verbose_flag; static void usage(void) { /* *INDENT-OFF* */ printf("Usage: selint [OPTIONS] FILE [...]\n"\ "Perform static code analysis on SELinux policy source.\n\n"); printf(" -c, --config=CONFIGFILE\tOverride default config with config\n"\ "\t\t\t\tspecified on command line. See\n"\ "\t\t\t\tCONFIGURATION section for config file syntax.\n"\ " --color=COLOR_OPTION\tConfigure color output.\n"\ "\t\t\t\tOptions are on, off and auto (the default).\n"\ " --context=CONTEXT_PATH\tRecursively scan CONTEXT_PATH to find additional te and if\n"\ "\t\t\t\tfiles to parse, but not scan. SELint will assume the scanned policy files\n"\ "\t\t\t\tare intended to be compiled together with the context files.\n"\ "\t\t\t\tare intended to be compiled together with the context files. Implies -s.\n"\ " --debug-parser\t\tEnable debug output for the internal policy parser.\n"\ "\t\t\t\tVery noisy, useful to debug parsing failures.\n"\ " -d, --disable=CHECKID\t\tDisable check with the given ID.\n"\ " -e, --enable=CHECKID\t\tEnable check with the given ID.\n"\ " -E, --only-enabled\t\tOnly run checks that are explicitly enabled with\n"\ "\t\t\t\tthe --enable option.\n"\ " --full-path\t\tPrint full path for files.\n"\ " -F, --fail\t\t\tExit with a non-zero value if any issue was found.\n"\ " -h, --help\t\t\tDisplay this menu.\n"\ " -l, --level=LEVEL\t\tOnly list errors with a severity level at or\n"\ "\t\t\t\tgreater than LEVEL. Options are C (convention), S (style),\n"\ "\t\t\t\tW (warning), E (error), F (fatal error).\n"\ " --scan-hidden-dirs\tScan hidden directories.\n"\ "\t\t\t\tBy default hidden directories (like '.git') are skipped in recursive mode.\n"\ " -s, --source\t\t\tRun in \"source mode\" to scan a policy source repository\n"\ "\t\t\t\tthat is designed to compile into a full system policy.\n"\ " -S, --summary\t\t\tDisplay a summary of issues found after running the analysis.\n"\ " --summary-only\t\tOnly display a summary of issues found after running the analysis.\n"\ "\t\t\t\tDo not show the individual findings. Implies -S.\n"\ " -r, --recursive\t\tScan recursively and check all SELinux policy files found.\n"\ " -v, --verbose\t\t\tEnable verbose output.\n"\ " -V, --version\t\t\tShow version information and exit.\n" ); /* *INDENT-ON* */ } #define WARN_ON_INVALID_CHECK_ID(id, desc)\ if (!is_valid_check(id)) {\ printf("%sWarning%s: %s, %s, is not a valid check id.\n", color_warning(), color_reset(), id, desc);\ } int main(int argc, char **argv) { char severity = '\0'; const char *config_filename = NULL; int source_flag = 0; int recursive_scan = 0; int only_enabled = 0; int exit_code = EX_OK; int summary_flag = 0; int fail_on_finding = 0; int scan_hidden_dirs = 0; struct string_list *context_paths = NULL; char color = 0; // 0 auto, 1 off, 2 on struct string_list *config_disabled_checks = NULL; struct string_list *config_enabled_checks = NULL; struct string_list *cl_disabled_checks = NULL; struct string_list *cl_enabled_checks = NULL; struct string_list *custom_fc_macros = NULL; struct string_list *cl_e_cursor = NULL; struct string_list *cl_d_cursor = NULL; yydebug = 0; while (1) { static const struct option long_options[] = { { "config", required_argument, NULL, 'c' }, { "context", required_argument, NULL, CONTEXT_ID }, { "debug-parser", no_argument, NULL, DEBUG_PARSER_ID }, { "disable", required_argument, NULL, 'd' }, { "enable", required_argument, NULL, 'e' }, { "fail", no_argument, NULL, 'F' }, { "full-path", no_argument, NULL, FULL_PATH_ID }, { "only-enabled", no_argument, NULL, 'E' }, { "help", no_argument, NULL, 'h' }, { "level", required_argument, NULL, 'l' }, { "modules-conf", required_argument, NULL, 'm' }, { "recursive", no_argument, NULL, 'r' }, { "source", no_argument, NULL, 's' }, { "summary", no_argument, NULL, 'S' }, { "color", required_argument, NULL, COLOR_ID }, { "scan-hidden-dirs", no_argument, NULL, SCAN_HIDDEN_DIRS_ID }, { "summary-only", no_argument, NULL, SUMMARY_ONLY_ID }, { "version", no_argument, NULL, 'V' }, { "verbose", no_argument, &verbose_flag, 1 }, { 0, 0, 0, 0 } }; int option_index = 0; int c = getopt_long(argc, argv, "c:d:e:EFhl:mrsSVv", long_options, &option_index); if (c == -1) { break; } switch (c) { case 0: break; case 'c': // Specify config file config_filename = optarg; break; case CONTEXT_ID: // Specify a path for context files if (!context_paths) { context_paths = sl_from_str(optarg); } else { append_to_sl(context_paths, optarg); } // Don't parse system devel policies if a context is given source_flag = 1; break; case COLOR_ID: if (0 == strcmp(optarg, "on")) { color = 2; } else if (0 == strcmp(optarg, "off")) { color = 1; } else if (0 == strcmp(optarg, "auto")) { color = 0; } else { printf("Invalid argument '%s' given for option --color\n", optarg); usage(); exit(EX_USAGE); } break; case DEBUG_PARSER_ID: // Enable debug mode for the internal parser yydebug = 1; break; case 'd': // Disable a given check if (cl_d_cursor) { cl_d_cursor->next = xcalloc(1, sizeof(struct string_list)); cl_d_cursor = cl_d_cursor->next; } else { cl_d_cursor = xcalloc(1, sizeof(struct string_list)); cl_disabled_checks = cl_d_cursor; } cl_d_cursor->string = xstrdup(optarg); break; case 'e': // Enable a given check if (cl_e_cursor) { cl_e_cursor->next = xcalloc(1, sizeof(struct string_list)); cl_e_cursor = cl_e_cursor->next; } else { cl_e_cursor = xcalloc(1, sizeof(struct string_list)); cl_enabled_checks = cl_e_cursor; } cl_e_cursor->string = xstrdup(optarg); break; case 'E': // Only run checks enabled by the --enable flag. only_enabled = 1; break; case FULL_PATH_ID: // Display full paths for files full_path = true; break; case 'F': // Exit non-zero if any issue was found fail_on_finding = 1; break; case 'h': // Display usage info and exit usage(); exit(0); case 'l': // Set the severity level severity = optarg[0]; if (!is_valid_severity(severity)) { printf("Invalid argument '%s' given for option --level\n", optarg); usage(); exit(EX_USAGE); } break; case 'm': // Specify a modules.conf file. (Not in the README) // TODO break; case 'r': // Scan recursively for files to parse recursive_scan = 1; break; case 's': // Run in source mode source_flag = 1; break; case SCAN_HIDDEN_DIRS_ID: // Scan hidden directories in recursive mode scan_hidden_dirs = 1; break; case SUMMARY_ONLY_ID: // Do not display individual findings suppress_output = 1; // FALLTHRU case 'S': // Display a summary at the end of the run summary_flag = 1; break; case 'V': // Output version info and exit printf("SELint %s\n", VERSION); exit(0); case 'v': // Run in verbose mode verbose_flag = 1; break; case '?': usage(); exit(EX_USAGE); } } print_if_verbose("Verbose mode enabled\n"); if (color == 2 || (color == 0 && isatty(STDOUT_FILENO))) { color_enable(); print_if_verbose("Color output enabled\n"); } if (source_flag) { print_if_verbose("Source mode enabled\n"); if (!recursive_scan) { printf("%sNote%s: Source mode enabled without recursive flag (only explicit specified files will be checked).\n", color_note(), color_reset()); } } for (const struct string_list * cur = cl_disabled_checks; cur; cur = cur->next) { WARN_ON_INVALID_CHECK_ID(cur->string, "disabled on command line"); } for (const struct string_list * cur = cl_enabled_checks; cur; cur = cur->next) { WARN_ON_INVALID_CHECK_ID(cur->string, "enabled on command line"); } if (config_filename && 0 != access(config_filename, R_OK)) { printf("%sError%s: No configuration file found at '%s'!\n", color_error(), color_reset(), config_filename); exit(EX_USAGE); } else if (!config_filename) { config_filename = SYSCONFDIR "/selint.conf"; // Default install path if (0 != access(config_filename, R_OK)) { //No default config found print_if_verbose( "No config specified and could not find default config at %s.\n", config_filename); config_filename = NULL; } } struct config_check_data ccd = { ORDER_LAX, {}, true, true, NULL }; if (config_filename) { char cfg_severity; if (SELINT_SUCCESS != parse_config(config_filename, source_flag, &cfg_severity, &config_disabled_checks, &config_enabled_checks, &custom_fc_macros, &ccd)) { // Error message printed by parse_config() exit(EX_CONFIG); } if (severity == '\0') { severity = cfg_severity; } } else { // If there is no config, we should assume the existence of normal users and // roles that we wouldn't otherwise know about insert_into_decl_map("system_u", "__assumed__", DECL_USER); insert_into_decl_map("object_r", "__assumed__", DECL_ROLE); } for (const struct string_list *config_check_id = config_disabled_checks; config_check_id; config_check_id = config_check_id->next) { WARN_ON_INVALID_CHECK_ID(config_check_id->string, "disabled in config"); } for (const struct string_list *config_check_id = config_enabled_checks; config_check_id; config_check_id = config_check_id->next) { WARN_ON_INVALID_CHECK_ID(config_check_id->string, "enabled in config"); } if (only_enabled && !cl_enabled_checks) { printf("%sError%s: no warning enabled!\n", color_error(), color_reset()); exit(EX_USAGE); } if (severity == '\0') { severity = 'C'; } print_if_verbose("Severity level set to %c\n", severity); if (optind >= argc) { usage(); exit(EX_USAGE); } struct policy_file_list *te_files = xcalloc(1, sizeof(struct policy_file_list)); struct policy_file_list *if_files = xcalloc(1, sizeof(struct policy_file_list)); struct policy_file_list *fc_files = xcalloc(1, sizeof(struct policy_file_list)); struct policy_file_list *context_te_files = xcalloc(1, sizeof(struct policy_file_list)); struct policy_file_list *context_if_files = xcalloc(1, sizeof(struct policy_file_list)); char **paths = xmalloc(sizeof(char *) * (unsigned)argc - (unsigned)optind + 2); int i = 0; while (optind < argc) { print_if_verbose("Path added to scan: '%s'\n", argv[optind]); paths[i++] = argv[optind++]; } paths[i] = NULL; FTS *ftsp = fts_open(paths, FTS_PHYSICAL | FTS_NOSTAT, NULL); FTSENT *file = fts_read(ftsp); char *modules_conf_path = NULL; char *obj_perm_sets_path = NULL; char *access_vector_path = NULL; struct string_list *global_cond_files = NULL; char *security_classes_path = NULL; while (file) { const char *suffix = (file->fts_pathlen > 3) ? (file->fts_path + file->fts_pathlen - 3) : NULL; if (suffix && !strcmp(suffix, ".te")) { file_list_push_back(te_files, make_policy_file(file->fts_path, NULL)); } else if (suffix && !strcmp(suffix, ".if")) { file_list_push_back(if_files, make_policy_file(file->fts_path, NULL)); char *mod_name = xstrdup(file->fts_name); mod_name[file->fts_namelen - 3] = '\0'; insert_into_mod_layers_map(mod_name, file->fts_parent->fts_name); free(mod_name); } else if (suffix && !strcmp(suffix, ".fc")) { file_list_push_back(fc_files, make_policy_file(file->fts_path, NULL)); } else if (source_flag && !strcmp(file->fts_name, "modules.conf")) { // TODO: Make modules.conf name configurable if (modules_conf_path) { printf("%sWarning%s: multiple module.conf files found (using %s, ignoring %s)\n", color_warning(), color_reset(), modules_conf_path, file->fts_path); } else { modules_conf_path = xstrdup(file->fts_path); } } else if (source_flag && !strcmp(file->fts_name, "obj_perm_sets.spt")) { // TODO: Make obj_perm_sets.spt name configurable if (obj_perm_sets_path) { printf("%sWarning%s: multiple obj_perm_sets.spt files found (using %s, ignoring %s)\n", color_warning(), color_reset(), obj_perm_sets_path, file->fts_path); } else { obj_perm_sets_path = xstrdup(file->fts_path); } } else if (source_flag && !strcmp(file->fts_name, "access_vectors")) { // TODO: Make access_vectors name configurable if (access_vector_path) { printf("%sWarning%s: multiple access_vectors files found (using %s, ignoring %s)\n", color_warning(), color_reset(), access_vector_path, file->fts_path); } else { access_vector_path = xstrdup(file->fts_path); } } else if (source_flag && (!strcmp(file->fts_name, "global_booleans") || !strcmp(file->fts_name, "global_tunables"))) { // TODO: Make names configurable global_cond_files = concat_string_lists(global_cond_files, sl_from_str(file->fts_path)); } else if (source_flag && !strcmp(file->fts_name, "security_classes")) { // TODO: Make security_classes name configurable security_classes_path = strdup(file->fts_path); } else { // Directories might get traversed twice: preorder and final visit. // Print only the final visit if (file->fts_info != FTS_D) { if (recursive_scan) { print_if_verbose("Skipping %s which is not a policy file\n", file->fts_path); } else { printf("%sNote%s: Skipping %s which is not a policy file\n", color_note(), color_reset(), file->fts_path); } } if (!recursive_scan) { fts_set(ftsp, file, FTS_SKIP); } if (!scan_hidden_dirs && file->fts_info == FTS_D && file->fts_name[0] == '.' && file->fts_name[1] != '.' && file->fts_name[1] != '\0') { print_if_verbose("Skipping hidden directory %s\n", file->fts_path); fts_set(ftsp, file, FTS_SKIP); } } file = fts_read(ftsp); } fts_close(ftsp); struct string_list *context_path_node = context_paths; while (context_path_node) { paths[0] = context_path_node->string; paths[1] = NULL; ftsp = fts_open(paths, FTS_PHYSICAL | FTS_NOSTAT, NULL); file = fts_read(ftsp); while (file) { const char *suffix = (file->fts_pathlen > 3) ? (file->fts_path + file->fts_pathlen - 3) : NULL; if (suffix && !strcmp(suffix, ".te")) { if (!file_name_in_file_list(file->fts_path, te_files)) { file_list_push_back(context_te_files, make_policy_file(file->fts_path, NULL)); } } else if (suffix && !strcmp(suffix, ".if")) { if (!file_name_in_file_list(file->fts_path, if_files)) { file_list_push_back(context_if_files, make_policy_file(file->fts_path, NULL)); } } else if (source_flag && !modules_conf_path && 0 == strcmp(file->fts_name, "modules.conf")) { modules_conf_path = xstrdup(file->fts_path); } else if (source_flag && !obj_perm_sets_path && 0 == strcmp(file->fts_name, "obj_perm_sets.spt")) { obj_perm_sets_path = xstrdup(file->fts_path); } else if (source_flag && !access_vector_path && 0 == strcmp(file->fts_name, "access_vectors")) { access_vector_path = xstrdup(file->fts_path); } else if (source_flag && !str_in_sl(file->fts_path, global_cond_files) && (0 == strcmp(file->fts_name, "global_booleans") || 0 == strcmp(file->fts_name, "global_tunables"))) { global_cond_files = concat_string_lists(global_cond_files, sl_from_str(file->fts_path)); } else if (source_flag && !security_classes_path && 0 == strcmp(file->fts_name, "security_classes")) { security_classes_path = strdup(file->fts_path); } file = fts_read(ftsp); } fts_close(ftsp); context_path_node = context_path_node->next; } free_string_list(context_paths); free(paths); // Load object classes and permissions if (source_flag) { if (access_vector_path) { enum selint_error res = load_access_vectors_source(access_vector_path); if (res != SELINT_SUCCESS) { printf("%sWarning%s: Failed to parse access_vectors from %s: %d\n", color_warning(), color_reset(), access_vector_path, res); } else { print_if_verbose("Loaded classes and permissions from %s\n", access_vector_path); } } else { printf("%sWarning%s: Failed to locate access_vectors file.\n", color_warning(), color_reset()); } if (security_classes_path) { enum selint_error res = load_security_classes_source(security_classes_path); if (res != SELINT_SUCCESS) { printf("%sWarning%s: Failed to parse security_classes from %s: %d\n", color_warning(), color_reset(), security_classes_path, res); } else { print_if_verbose("Loaded security classes from %s\n", security_classes_path); userspace_class_support = 1; } } else { printf("%sWarning%s: Failed to locate security_classes file.\n", color_warning(), color_reset()); } if (modules_conf_path) { enum selint_error res = load_modules_source(modules_conf_path); if (res != SELINT_SUCCESS) { printf("%sWarning%s: Failed to load modules from %s: %d\n", color_warning(), color_reset(), modules_conf_path, res); } else { print_if_verbose("Loaded modules from %s\n", modules_conf_path); } } else { printf("%sWarning%s: Failed to locate modules.conf file.\n", color_warning(), color_reset()); } if (obj_perm_sets_path) { enum selint_error res = load_obj_perm_sets_source(obj_perm_sets_path); if (res != SELINT_SUCCESS) { printf("%sWarning%s: Failed to permission and class set macros from %s: %d\n", color_warning(), color_reset(), obj_perm_sets_path, res); } else { print_if_verbose("Loaded permission and class set macros from %s\n", obj_perm_sets_path); } } else { printf("%sWarning%s: Failed to locate obj_perm_sets.spt file.\n", color_warning(), color_reset()); } if (global_cond_files) { enum selint_error res = load_global_conditions(global_cond_files); if (res != SELINT_SUCCESS) { printf("%sWarning%s: Failed to parse global conditions: %d\n", color_warning(), color_reset(), res); } else { print_if_verbose("Loaded global conditions\n"); } } else { printf("%sWarning%s: Failed to locate global conditions files.\n", color_warning(), color_reset()); } } else { enum selint_error r = load_access_vectors_kernel("/sys/fs/selinux/class"); if (r != SELINT_SUCCESS) { if (r == SELINT_IO_ERROR) { printf("%sNote%s: Failed to load classes and perms probably due to running on a SELinux disabled system.\n", color_note(), color_reset()); } else { printf("%sWarning%s: Failed to load classes and perms from current kernel.\n", color_warning(), color_reset()); } } load_modules_normal(); enum selint_error res = load_devel_headers(context_if_files); if (res != SELINT_SUCCESS) { printf("%sWarning%s: Failed to load SELinux development header files.\n", color_warning(), color_reset()); } } /* Delay until support files have been parsed for check conditions. */ struct checks *ck = register_checks(severity, config_enabled_checks, config_disabled_checks, cl_enabled_checks, cl_disabled_checks, only_enabled); if (!ck) { printf("%sError%s: Failed to register checks (bad configuration)\n", color_error(), color_reset()); free_file_list(te_files); free_file_list(if_files); free_file_list(fc_files); free_file_list(context_te_files); free_file_list(context_if_files); free(obj_perm_sets_path); free(access_vector_path); free(security_classes_path); free(modules_conf_path); free_string_list(global_cond_files); free_string_list(custom_fc_macros); return EX_CONFIG; } free(obj_perm_sets_path); free(access_vector_path); free(security_classes_path); free(modules_conf_path); free_string_list(global_cond_files); enum selint_error res = run_analysis(ck, te_files, if_files, fc_files, context_te_files, context_if_files, custom_fc_macros, &ccd); switch (res) { case SELINT_SUCCESS: if (summary_flag) { display_run_summary(ck); } break; case SELINT_PARSE_ERROR: printf("%sError%s: Failed to parse files\n", color_error(), color_reset()); exit_code = EX_SOFTWARE; break; default: printf("%sError%s: Internal error: %d\n", color_error(), color_reset(), res); exit_code = EX_SOFTWARE; } if (config_enabled_checks) { free_string_list(config_enabled_checks); } if (config_disabled_checks) { free_string_list(config_disabled_checks); } if (cl_enabled_checks) { free_string_list(cl_enabled_checks); } if (cl_disabled_checks) { free_string_list(cl_disabled_checks); } free_checks(ck); free_file_list(te_files); free_file_list(if_files); free_file_list(fc_files); free_file_list(context_te_files); free_file_list(context_if_files); free_string_list(custom_fc_macros); free_selint_config(&ccd); if (fail_on_finding && found_issue && exit_code == EX_OK) { return EX_DATAERR; } return exit_code; } selint-1.5.1/src/maps.c000066400000000000000000000423641475050262500147260ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "maps.h" #include "xalloc.h" #if defined(__clang__) && defined(__clang_major__) && (__clang_major__ >= 4) #if (__clang_major__ >= 12) #define no_sanitize_unsigned_integer_ __attribute__((no_sanitize("unsigned-integer-overflow", "unsigned-shift-base"))) #else #define no_sanitize_unsigned_integer_ __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif #else #define no_sanitize_unsigned_integer_ #endif int userspace_class_support = 0; static struct hash_elem *type_map = NULL; static struct hash_elem *role_map = NULL; static struct hash_elem *user_map = NULL; static struct hash_elem *attr_type_map = NULL; static struct hash_elem *attr_role_map = NULL; static struct hash_elem *bool_map = NULL; static struct hash_elem *class_map = NULL; static struct hash_elem *perm_map = NULL; static struct hash_elem *mods_map = NULL; static struct hash_elem *mod_layers_map = NULL; static struct if_hash_elem *interfaces_map = NULL; static struct bool_hash_elem *userspace_class_map = NULL; static struct sl_hash_elem *permmacros_map = NULL; static struct template_hash_elem *template_map = NULL; no_sanitize_unsigned_integer_ static struct hash_elem *look_up_hash_elem(const char *name, enum decl_flavor flavor) { if (!name) { return NULL; } struct hash_elem *decl; switch (flavor) { case DECL_TYPE: HASH_FIND(hh_type, type_map, name, strlen(name), decl); break; case DECL_ROLE: HASH_FIND(hh_role, role_map, name, strlen(name), decl); break; case DECL_USER: HASH_FIND(hh_user, user_map, name, strlen(name), decl); break; case DECL_ATTRIBUTE: HASH_FIND(hh_attr_type, attr_type_map, name, strlen(name), decl); break; case DECL_ATTRIBUTE_ROLE: HASH_FIND(hh_attr_role, attr_role_map, name, strlen(name), decl); break; case DECL_BOOL: HASH_FIND(hh_bool, bool_map, name, strlen(name), decl); break; case DECL_CLASS: HASH_FIND(hh_class, class_map, name, strlen(name), decl); break; case DECL_PERM: HASH_FIND(hh_perm, perm_map, name, strlen(name), decl); break; default: decl = NULL; } return decl; } no_sanitize_unsigned_integer_ void insert_into_decl_map(const char *name, const char *module_name, enum decl_flavor flavor) { struct hash_elem *decl = look_up_hash_elem(name, flavor); if (decl == NULL) { // Item not in hash table already decl = xmalloc(sizeof(struct hash_elem)); decl->key = xstrdup(name); decl->val = xstrdup(module_name); switch (flavor) { case DECL_TYPE: HASH_ADD_KEYPTR(hh_type, type_map, decl->key, strlen(decl->key), decl); break; case DECL_ROLE: HASH_ADD_KEYPTR(hh_role, role_map, decl->key, strlen(decl->key), decl); break; case DECL_USER: HASH_ADD_KEYPTR(hh_user, user_map, decl->key, strlen(decl->key), decl); break; case DECL_ATTRIBUTE: HASH_ADD_KEYPTR(hh_attr_type, attr_type_map, decl->key, strlen(decl->key), decl); break; case DECL_ATTRIBUTE_ROLE: HASH_ADD_KEYPTR(hh_attr_role, attr_role_map, decl->key, strlen(decl->key), decl); break; case DECL_BOOL: HASH_ADD_KEYPTR(hh_bool, bool_map, decl->key, strlen(decl->key), decl); break; case DECL_CLASS: HASH_ADD_KEYPTR(hh_class, class_map, decl->key, strlen(decl->key), decl); break; case DECL_PERM: HASH_ADD_KEYPTR(hh_perm, perm_map, decl->key, strlen(decl->key), decl); break; default: free(decl->key); free(decl->val); free(decl); return; } } //TODO: else report error? } const char *look_up_in_decl_map(const char *name, enum decl_flavor flavor) { struct hash_elem *decl = look_up_hash_elem(name, flavor); if (decl == NULL) { return NULL; } else { return decl->val; } } no_sanitize_unsigned_integer_ void insert_into_mods_map(const char *mod_name, const char *status) { struct hash_elem *mod; HASH_FIND(hh_mods, mods_map, mod_name, strlen(mod_name), mod); if (!mod) { mod = xmalloc(sizeof(struct hash_elem)); mod->key = xstrdup(mod_name); mod->val = xstrdup(status); HASH_ADD_KEYPTR(hh_mods, mods_map, mod->key, strlen(mod->key), mod); } } no_sanitize_unsigned_integer_ const char *look_up_in_mods_map(const char *mod_name) { struct hash_elem *mod; HASH_FIND(hh_mods, mods_map, mod_name, strlen(mod_name), mod); if (mod == NULL) { return NULL; } else { return mod->val; } } no_sanitize_unsigned_integer_ void insert_into_mod_layers_map(const char *mod_name, const char *layer) { struct hash_elem *mod; HASH_FIND(hh_mod_layers, mod_layers_map, mod_name, strlen(mod_name), mod); if (!mod) { mod = xmalloc(sizeof(struct hash_elem)); mod->key = xstrdup(mod_name); mod->val = xstrdup(layer); HASH_ADD_KEYPTR(hh_mod_layers, mod_layers_map, mod->key, strlen(mod->key), mod); } } no_sanitize_unsigned_integer_ const char *look_up_in_mod_layers_map(const char *mod_name) { struct hash_elem *mod; HASH_FIND(hh_mod_layers, mod_layers_map, mod_name, strlen(mod_name), mod); if (mod == NULL) { return NULL; } else { return mod->val; } } no_sanitize_unsigned_integer_ void insert_into_ifs_map(const char *if_name, const char *mod_name) { struct if_hash_elem *if_call; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), if_call); if (!if_call) { if_call = xmalloc(sizeof(struct if_hash_elem)); if_call->name = xstrdup(if_name); if_call->module = xstrdup(mod_name); if_call->flags = 0; HASH_ADD_KEYPTR(hh_interfaces, interfaces_map, if_call->name, strlen(if_call->name), if_call); } else { free(if_call->module); if_call->module = xstrdup(mod_name); } } no_sanitize_unsigned_integer_ const char *look_up_in_ifs_map(const char *if_name) { struct if_hash_elem *if_call; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), if_call); if (if_call == NULL) { return NULL; } else { return if_call->module; } } unsigned int decl_map_count(enum decl_flavor flavor) { switch (flavor) { case DECL_TYPE: return HASH_CNT(hh_type, type_map); case DECL_ATTRIBUTE: return HASH_CNT(hh_attr_type, attr_type_map); case DECL_ATTRIBUTE_ROLE: return HASH_CNT(hh_attr_role, attr_role_map); case DECL_ROLE: return HASH_CNT(hh_role, role_map); case DECL_USER: return HASH_CNT(hh_user, user_map); case DECL_BOOL: return HASH_CNT(hh_bool, bool_map); case DECL_CLASS: return HASH_CNT(hh_class, class_map); case DECL_PERM: return HASH_CNT(hh_perm, perm_map); default: return 0; } } no_sanitize_unsigned_integer_ void mark_userspace_class(const char *class_name) { struct bool_hash_elem *userspace_class; HASH_FIND(hh_userspace_class, userspace_class_map, class_name, strlen(class_name), userspace_class); if (!userspace_class) { userspace_class = xmalloc(sizeof(struct bool_hash_elem)); userspace_class->key = xstrdup(class_name); userspace_class->val = 1; HASH_ADD_KEYPTR(hh_userspace_class, userspace_class_map, userspace_class->key, strlen(userspace_class->key), userspace_class); } else { userspace_class->val = 1; } } no_sanitize_unsigned_integer_ int is_userspace_class(const char *class_name, const struct string_list *permissions) { struct bool_hash_elem *userspace_class; HASH_FIND(hh_userspace_class, userspace_class_map, class_name, strlen(class_name), userspace_class); if (userspace_class && userspace_class->val == 1) { return 1; } // the system class might be used by systemd depending on the permission if (0 != strcmp(class_name, "system")) { return 0; } for (const struct string_list *p = permissions; p; p = p->next) { // if permission is not one of the kernel ones // treat system as userspace class if (0 != strcmp(p->string, "ipc_info") && 0 != strcmp(p->string, "syslog_read") && 0 != strcmp(p->string, "syslog_mod") && 0 != strcmp(p->string, "syslog_console") && 0 != strcmp(p->string, "module_request") && 0 != strcmp(p->string, "module_load") && 0 != strcmp(p->string, "*") && 0 != strcmp(p->string, "~")) { return 1; } } return 0; } no_sanitize_unsigned_integer_ void mark_transform_if(const char *if_name) { struct if_hash_elem *transform_if; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), transform_if); if (!transform_if) { transform_if = xmalloc(sizeof(struct if_hash_elem)); transform_if->name = xstrdup(if_name); transform_if->module = NULL; transform_if->flags = TRANSFORM_IF; HASH_ADD_KEYPTR(hh_interfaces, interfaces_map, transform_if->name, strlen(transform_if->name), transform_if); } else { transform_if->flags |= TRANSFORM_IF; } } no_sanitize_unsigned_integer_ int is_transform_if(const char *if_name) { struct if_hash_elem *transform_if; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), transform_if); if (transform_if && (transform_if->flags & TRANSFORM_IF)) { return 1; } else { return 0; } } no_sanitize_unsigned_integer_ void mark_filetrans_if(const char *if_name) { struct if_hash_elem *filetrans_if; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), filetrans_if); if (!filetrans_if) { filetrans_if = xmalloc(sizeof(struct if_hash_elem)); filetrans_if->name = xstrdup(if_name); filetrans_if->module = NULL; filetrans_if->flags = FILETRANS_IF; HASH_ADD_KEYPTR(hh_interfaces, interfaces_map, filetrans_if->name, strlen(filetrans_if->name), filetrans_if); } else { filetrans_if->flags |= FILETRANS_IF; } } no_sanitize_unsigned_integer_ int is_filetrans_if(const char *if_name) { struct if_hash_elem *filetrans_if; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), filetrans_if); if (filetrans_if && (filetrans_if->flags & FILETRANS_IF)) { return 1; } else { return 0; } } no_sanitize_unsigned_integer_ void mark_role_if(const char *if_name) { struct if_hash_elem *role_if; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), role_if); if (!role_if) { role_if = xmalloc(sizeof(struct if_hash_elem)); role_if->name = xstrdup(if_name); role_if->module = NULL; role_if->flags = ROLE_IF; HASH_ADD_KEYPTR(hh_interfaces, interfaces_map, role_if->name, strlen(role_if->name), role_if); } else { role_if->flags |= ROLE_IF; } } no_sanitize_unsigned_integer_ int is_role_if(const char *if_name) { struct if_hash_elem *role_if; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), role_if); if (role_if && (role_if->flags & ROLE_IF)) { return 1; } else { return 0; } } no_sanitize_unsigned_integer_ void mark_used_if(const char *if_name) { struct if_hash_elem *used_if; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), used_if); if (!used_if) { used_if = xmalloc(sizeof(struct if_hash_elem)); used_if->name = xstrdup(if_name); used_if->module = NULL; used_if->flags = USED_IF; HASH_ADD_KEYPTR(hh_interfaces, interfaces_map, used_if->name, strlen(used_if->name), used_if); } else { used_if->flags |= USED_IF; } } no_sanitize_unsigned_integer_ int is_used_if(const char *if_name) { struct if_hash_elem *used_if; HASH_FIND(hh_interfaces, interfaces_map, if_name, strlen(if_name), used_if); if (used_if && (used_if->flags & USED_IF)) { return 1; } else { return 0; } } static void insert_decl(struct template_hash_elem *template, void *new_node) { if (template->declarations) { struct decl_list *cur = template->declarations; while (cur->next) { cur = cur->next; } cur->next = (struct decl_list *)new_node; } else { template->declarations = (struct decl_list *)new_node; } } static void insert_call(struct template_hash_elem *template, void *new_node) { if (template->calls) { struct if_call_list *cur = template->calls; while (cur->next) { cur = cur->next; } cur->next = (struct if_call_list *)new_node; } else { template->calls = (struct if_call_list *)new_node; } } static void insert_noop(__attribute__((unused)) struct template_hash_elem *template, __attribute__((unused)) void *new_node) { return; } no_sanitize_unsigned_integer_ static void insert_into_template_map(const char *name, void *new_node, void (*insertion_func)(struct template_hash_elem *, void *)) { struct template_hash_elem *template; HASH_FIND(hh, template_map, name, strlen(name), template); if (template == NULL) { template = xmalloc(sizeof(struct template_hash_elem)); template->name = xstrdup(name); template->declarations = NULL; template->calls = NULL; HASH_ADD_KEYPTR(hh, template_map, template->name, strlen(template->name), template); } insertion_func(template, new_node); } void insert_template_into_template_map(const char *name) { insert_into_template_map(name, NULL, insert_noop); } void insert_decl_into_template_map(const char *name, enum decl_flavor flavor, const char *declaration) { struct declaration_data *new_data = xmalloc(sizeof(struct declaration_data)); new_data->flavor = flavor; new_data->name = xstrdup(declaration); new_data->attrs = NULL; //Not needed struct decl_list *new_node = xmalloc(sizeof(struct decl_list)); new_node->decl = new_data; new_node->next = NULL; insert_into_template_map(name, new_node, insert_decl); } void insert_call_into_template_map(const char *name, struct if_call_data *call) { struct if_call_list *new_node = xmalloc(sizeof(struct if_call_list)); new_node->call = call; new_node->next = NULL; insert_into_template_map(name, new_node, insert_call); } no_sanitize_unsigned_integer_ const struct template_hash_elem *look_up_in_template_map(const char *name) { struct template_hash_elem *template; HASH_FIND(hh, template_map, name, strlen(name), template); return template; } const struct decl_list *look_up_decl_in_template_map(const char *name) { const struct template_hash_elem *template = look_up_in_template_map(name); if (template) { return template->declarations; } else { return NULL; } } const struct if_call_list *look_up_call_in_template_map(const char *name) { const struct template_hash_elem *template = look_up_in_template_map(name); if (template) { return template->calls; } else { return NULL; } } no_sanitize_unsigned_integer_ void insert_into_permmacros_map(const char *name, struct string_list *permissions) { struct sl_hash_elem *perm_macro; HASH_FIND(hh_permmacros, permmacros_map, name, strlen(name), perm_macro); if (!perm_macro) { perm_macro = xmalloc(sizeof(struct sl_hash_elem)); perm_macro->key = xstrdup(name); perm_macro->val = permissions; HASH_ADD_KEYPTR(hh_permmacros, permmacros_map, perm_macro->key, strlen(perm_macro->key), perm_macro); } } no_sanitize_unsigned_integer_ const struct string_list *look_up_in_permmacros_map(const char *name) { struct sl_hash_elem *perm_macro; HASH_FIND(hh_permmacros, permmacros_map, name, strlen(name), perm_macro); if (perm_macro == NULL) { return NULL; } else { return perm_macro->val; } } void visit_all_in_permmacros_map(void (*visitor)(const char *name, const struct string_list *permissions)) { const struct sl_hash_elem *cur_sl, *tmp_sl; HASH_ITER(hh_permmacros, permmacros_map, cur_sl, tmp_sl) { visitor(cur_sl->key, cur_sl->val); } } unsigned int permmacros_map_count(void) { return HASH_CNT(hh_permmacros, permmacros_map); } #define FREE_MAP(mn) HASH_ITER(hh_ ## mn, mn ## _map, cur_decl, tmp_decl) { \ HASH_DELETE(hh_ ## mn, mn ## _map, cur_decl); \ free(cur_decl->key); \ free(cur_decl->val); \ free(cur_decl); \ } \ #define FREE_BOOL_MAP(mn) HASH_ITER(hh_ ## mn, mn ## _map, cur_bool, tmp_bool) { \ HASH_DELETE(hh_ ## mn, mn ## _map, cur_bool); \ free(cur_bool->key); \ free(cur_bool); \ } \ #define FREE_IF_MAP(mn) HASH_ITER(hh_ ## mn, mn ## _map, cur_if, tmp_if) { \ HASH_DELETE(hh_ ## mn, mn ## _map, cur_if); \ free(cur_if->name); \ free(cur_if->module); \ free(cur_if); \ } \ void free_all_maps(void) { struct hash_elem *cur_decl, *tmp_decl; FREE_MAP(type); FREE_MAP(role); FREE_MAP(user); FREE_MAP(attr_type); FREE_MAP(attr_role); FREE_MAP(bool); FREE_MAP(class); FREE_MAP(perm); FREE_MAP(mods); FREE_MAP(mod_layers); struct if_hash_elem *cur_if, *tmp_if; FREE_IF_MAP(interfaces); struct bool_hash_elem *cur_bool, *tmp_bool; FREE_BOOL_MAP(userspace_class); struct sl_hash_elem *cur_sl, *tmp_sl; HASH_ITER(hh_permmacros, permmacros_map, cur_sl, tmp_sl) { HASH_DELETE(hh_permmacros, permmacros_map, cur_sl); free(cur_sl->key); free_string_list(cur_sl->val); free(cur_sl); } struct template_hash_elem *cur_template, *tmp_template; HASH_ITER(hh, template_map, cur_template, tmp_template) { HASH_DELETE(hh, template_map, cur_template); free(cur_template->name); free_decl_list(cur_template->declarations); free_if_call_list(cur_template->calls); free(cur_template); } } selint-1.5.1/src/maps.h000066400000000000000000000070461475050262500147310ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MAPS_H #define MAPS_H #include #include "tree.h" #include "selint_error.h" struct hash_elem { char *key; char *val; UT_hash_handle hh_type, hh_role, hh_user, hh_attr_type, hh_attr_role, hh_bool, hh_class, hh_perm, hh_mods, hh_mod_layers; }; struct bool_hash_elem { char *key; int val; UT_hash_handle hh_userspace_class; }; struct sl_hash_elem { char *key; struct string_list *val; UT_hash_handle hh_permmacros; }; #define TRANSFORM_IF (1u << 0) #define FILETRANS_IF (1u << 1) #define ROLE_IF (1u << 2) #define USED_IF (1u << 3) struct if_hash_elem { char *name; char *module; uint8_t flags; UT_hash_handle hh_interfaces; }; struct template_hash_elem { char *name; struct decl_list *declarations; struct if_call_list *calls; UT_hash_handle hh; }; void insert_into_decl_map(const char *name, const char *module_name, enum decl_flavor flavor); const char *look_up_in_decl_map(const char *name, enum decl_flavor flavor); void insert_into_mods_map(const char *mod_name, const char *status); const char *look_up_in_mods_map(const char *mod_name); void insert_into_mod_layers_map(const char *mod_name, const char *layer); const char *look_up_in_mod_layers_map(const char *mod_name); void insert_into_ifs_map(const char *if_name, const char *mod_name); const char *look_up_in_ifs_map(const char *if_name); void mark_userspace_class(const char *class_name); int is_userspace_class(const char *class_name, const struct string_list *permissions); extern int userspace_class_support; void mark_transform_if(const char *if_name); int is_transform_if(const char *if_name); void mark_filetrans_if(const char *if_name); int is_filetrans_if(const char *if_name); void mark_role_if(const char *if_name); int is_role_if(const char *if_name); void mark_used_if(const char *if_name); int is_used_if(const char *if_name); // Just generate a template entry in the map, but don't save any calls // or decls to it. This is helpful to know what is a template for certain // checks even if the template never calls or declares anything void insert_template_into_template_map(const char *name); void insert_decl_into_template_map(const char *name, enum decl_flavor flavor, const char *declaration); void insert_call_into_template_map(const char *name, struct if_call_data *call); const struct template_hash_elem *look_up_in_template_map(const char *name); const struct decl_list *look_up_decl_in_template_map(const char *name); const struct if_call_list *look_up_call_in_template_map(const char *name); void insert_into_permmacros_map(const char *name, struct string_list *permissions); const struct string_list *look_up_in_permmacros_map(const char *name); void visit_all_in_permmacros_map(void (*visitor)(const char *name, const struct string_list *permissions)); unsigned int permmacros_map_count(void); unsigned int decl_map_count(enum decl_flavor flavor); void free_all_maps(void); #endif selint-1.5.1/src/name_list.c000066400000000000000000000117271475050262500157400ustar00rootroot00000000000000/* * Copyright 2022 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "name_list.h" #include #include #include "tree.h" #include "xalloc.h" static bool is_compatible(enum name_flavor a, enum name_flavor b) { if (b == NAME_UNKNOWN) { return true; } switch (a) { case NAME_TYPE: return b == NAME_TYPE || b == NAME_TYPE_OR_ATTRIBUTE; case NAME_TYPEATTRIBUTE: return b == NAME_TYPEATTRIBUTE || b == NAME_TYPE_OR_ATTRIBUTE; case NAME_TYPE_OR_ATTRIBUTE: return b == NAME_TYPE || b == NAME_TYPEATTRIBUTE || b == NAME_TYPE_OR_ATTRIBUTE; case NAME_ROLE: return b == NAME_ROLE || b == NAME_ROLE_OR_ATTRIBUTE; case NAME_ROLEATTRIBUTE: return b == NAME_ROLEATTRIBUTE || b == NAME_ROLE_OR_ATTRIBUTE; case NAME_ROLE_OR_ATTRIBUTE: return b == NAME_ROLE || b == NAME_ROLEATTRIBUTE || b == NAME_ROLE_OR_ATTRIBUTE; case NAME_CLASS: return b == NAME_CLASS; case NAME_PERM: return b == NAME_PERM; case NAME_USER: return b == NAME_USER; case NAME_BOOL: return b == NAME_BOOL; case NAME_UNKNOWN: return true; default: // should never happen return 0; } } bool name_is_type(const struct name_data *name) { return is_compatible(name->flavor, NAME_TYPE); } bool name_is_typeattr(const struct name_data *name) { return is_compatible(name->flavor, NAME_TYPEATTRIBUTE); } bool name_is_role(const struct name_data *name) { return is_compatible(name->flavor, NAME_ROLE); } bool name_is_roleattr(const struct name_data *name) { return is_compatible(name->flavor, NAME_ROLEATTRIBUTE); } bool name_is_class(const struct name_data *name) { return is_compatible(name->flavor, NAME_CLASS); } bool name_list_contains_name(const struct name_list *nl, const struct name_data *name) { for (;nl; nl = nl->next) { if (!is_compatible(nl->data->flavor, name->flavor)) { continue; } if (0 == strcmp(nl->data->name, name->name)) { return true; } } return false; } struct name_list *name_list_from_sl_with_traits(const struct string_list *sl, enum name_flavor flavor, const struct string_list *traits) { if (!sl) { return NULL; } struct name_list *ret = xmalloc(sizeof(struct name_list)); struct name_list *cur = ret; while (sl) { struct name_data *data = xmalloc(sizeof(struct name_data)); data->name = xstrdup(sl->string); data->flavor = flavor; data->traits = copy_string_list(traits); cur->data = data; if (sl->next) { cur->next = xmalloc(sizeof(struct name_list)); } else { cur->next = NULL; } sl = sl->next; cur = cur->next; } return ret; } struct name_list *name_list_create(const char *name, enum name_flavor flavor) { struct name_data *data = xmalloc(sizeof(struct name_data)); data->name = xstrdup(name); data->flavor = flavor; data->traits = NULL; struct name_list *ret = xmalloc(sizeof(struct name_list)); ret->data = data; ret->next = NULL; return ret; } struct name_list *name_list_from_decl(const struct declaration_data *decl) { struct name_data *data = xmalloc(sizeof(struct name_data)); data->name = xstrdup(decl->name); data->traits = NULL; struct name_list *extra = NULL; switch (decl->flavor) { case DECL_TYPE: data->flavor = NAME_TYPE; extra = name_list_from_sl(decl->attrs, NAME_TYPEATTRIBUTE); break; case DECL_ATTRIBUTE: data->flavor = NAME_TYPEATTRIBUTE; break; case DECL_ATTRIBUTE_ROLE: data->flavor = NAME_ROLEATTRIBUTE; break; case DECL_ROLE: data->flavor = NAME_ROLE; break; case DECL_USER: data->flavor = NAME_USER; break; case DECL_CLASS: data->flavor = NAME_CLASS; data->traits = copy_string_list(decl->attrs); break; case DECL_PERM: data->flavor = NAME_PERM; break; case DECL_BOOL: data->flavor = NAME_BOOL; break; default: // should never happen data->flavor = NAME_UNKNOWN; break; } struct name_list *ret = xmalloc(sizeof(struct name_list)); ret->data = data; ret->next = extra; return ret; } struct name_list *concat_name_lists(struct name_list *head, struct name_list *tail) { if (!head) { return tail; } if (!tail) { return head; } struct name_list *cur = head; while (cur->next) { cur = cur->next; } cur->next = tail; return head; } void free_name_list(struct name_list *nl) { if (nl == NULL) { return; } struct name_list *cur = nl; while (cur) { struct name_list *to_free = cur; cur = cur->next; free(to_free->data->name); free_string_list(to_free->data->traits); free(to_free->data); free(to_free); } } selint-1.5.1/src/name_list.h000066400000000000000000000050061475050262500157360ustar00rootroot00000000000000/* * Copyright 2022 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NAME_LIST_H #define NAME_LIST_H #include #include "string_list.h" // avoid circular include with tree.h struct declaration_data; enum name_flavor { NAME_UNKNOWN, NAME_TYPE, NAME_TYPEATTRIBUTE, NAME_TYPE_OR_ATTRIBUTE, NAME_ROLE, NAME_ROLEATTRIBUTE, NAME_ROLE_OR_ATTRIBUTE, NAME_CLASS, NAME_PERM, NAME_USER, NAME_BOOL, }; struct name_data { enum name_flavor flavor; char *name; // flavor == NAME_CLASS: list of associated permissions struct string_list *traits; }; bool name_is_type(const struct name_data *name); bool name_is_typeattr(const struct name_data *name); bool name_is_role(const struct name_data *name); bool name_is_roleattr(const struct name_data *name); bool name_is_class(const struct name_data *name); struct name_list { struct name_data *data; struct name_list *next; }; // Create a name list with a single entry struct name_list *name_list_create(const char *name, enum name_flavor flavor); // Create a name list with identifiers from a string list and associate all with flavor struct name_list *name_list_from_sl_with_traits(const struct string_list *sl, enum name_flavor flavor, const struct string_list *traits); static inline struct name_list *name_list_from_sl(const struct string_list *sl, enum name_flavor flavor) { return name_list_from_sl_with_traits(sl, flavor, NULL); } // Concat two name lists, accepts NULL lists. // Note: freeing the returned list will free both original name lists struct name_list *concat_name_lists(struct name_list *head, struct name_list *tail); // Create a name list with a single entry from a declaration struct name_list *name_list_from_decl(const struct declaration_data *decl); bool name_list_contains_name(const struct name_list *nl, const struct name_data *name); void free_name_list(struct name_list *nl); #endif selint-1.5.1/src/ordering.c000066400000000000000000000756361475050262500156070ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE #include #include #include #include #include "ordering.h" #include "maps.h" #include "xalloc.h" #define SECTION_NON_ORDERED "_non_ordered" #define SECTION_DECLARATION "_declaration" static bool is_optional(const struct policy_node *node); static bool is_boolean(const struct policy_node *node); static bool is_tunable(const struct policy_node *node); static bool is_in_ifdef(const struct policy_node *node); // get next node, skip nodes marked with #selint-disable:C-001 static const struct policy_node *ordering_next(const struct policy_node *node) { const struct policy_node *next = node; do { next = dfs_next(next); } while (next && next->exceptions && strstr(next->exceptions, "C-001")); return next; } struct ordering_metadata *prepare_ordering_metadata(const struct check_data *data, const struct policy_node *head) { const struct policy_node *cur = head->next; // head is file. Order the contents size_t count = 0; struct section_data *sections = xcalloc(1, sizeof(struct section_data)); while (cur) { if (add_section_info(sections, get_section(cur), cur->lineno) == SELINT_BAD_ARG) { free(sections); return NULL; } count += 1; cur = ordering_next(cur); } calculate_average_lines(sections); struct ordering_metadata *ret = xcalloc(1, sizeof(struct ordering_metadata) + (count * sizeof(struct order_node))); ret->mod_name = data->mod_name; // Will only be needed for duration of check, so will remain allocated // until we are done with this copy ret->order_node_len = count; ret->sections = sections; // The nodes array will be populated during the LIS traversal return ret; } void calculate_longest_increasing_subsequence(const struct policy_node *head, struct ordering_metadata *ordering, enum order_difference_reason (*comp_func)(const struct ordering_metadata *o, const struct policy_node *first, const struct policy_node *second)) { struct order_node *nodes = ordering->nodes; int longest_seq = 0; int index = 0; const struct policy_node *cur = head->next; while (cur) { // Save the node in the array if (cur->flavor == NODE_START_BLOCK) { cur = ordering_next(cur); continue; } nodes[index].node = cur; // binary search sequences so far int low = 1; int high = longest_seq; while (low <= high) { int mid = low + (high - low + 1) / 2; // Ceiling if (comp_func(ordering, nodes[nodes[mid-1].end_of_seq].node, nodes[index].node) >= 0) { low = mid + 1; } else { high = mid - 1; } } // Now low should be 1 greater than the length of the longest // sequence that ends lower than the current number if (low <= 1) { nodes[index].seq_prev = -1; // No previous node } else { nodes[index].seq_prev = nodes[low - 2].end_of_seq; } nodes[low - 1].end_of_seq = index; if (low > longest_seq) { longest_seq = low; } index++; cur = ordering_next(cur); } // Mark LIS elements index = nodes[longest_seq - 1].end_of_seq; while (index != -1) { nodes[index].in_order = 1; index = nodes[index].seq_prev; } #ifdef DEBUG_INFO enum order_conf variant; if (comp_func == compare_nodes_refpolicy) { variant = ORDER_REF; } else if (comp_func == compare_nodes_refpolicy_lax) { variant = ORDER_LAX; } else if (comp_func == compare_nodes_refpolicy_light) { variant = ORDER_LIGHT; } else { variant = ORDER_REF; // Should never happen } for (size_t i=0; i< ordering->order_node_len; i++) { if(nodes[i].node) { const char *section = get_section(nodes[i].node); printf("Line: %u, Section %s: LSS: %s in-order=%u index=%zu seq_prev=%d end_of_seq=%d avg_line=%.1f\n", nodes[i].node->lineno, section, lss_to_string(get_local_subsection(ordering->mod_name, nodes[i].node, variant)), nodes[i].in_order, i, nodes[i].seq_prev, nodes[i].end_of_seq, get_avg_line_by_name(section, ordering->sections)); } } #endif } enum selint_error add_section_info(struct section_data *sections, const char *section_name, unsigned int lineno) { if (sections == NULL || section_name == NULL) { return SELINT_BAD_ARG; } struct section_data *cur = sections; if (sections->section_name != NULL) { while (0 != strcmp(cur->section_name, section_name)) { if (cur->next == NULL) { cur->next = xcalloc(1, sizeof(struct section_data)); cur = cur->next; break; } cur = cur->next; } } // cur is now the appropriate section_data node. If section_name is // NULL, then this is a new node if (!cur->section_name) { cur->section_name = xstrdup(section_name); } cur->lineno_count++; cur->lines_sum += lineno; return SELINT_SUCCESS; } const char *get_section(const struct policy_node *node) { if (!node) { return NULL; //Error } switch (node->flavor) { case NODE_TE_FILE: case NODE_IF_FILE: case NODE_FC_FILE: case NODE_SPT_FILE: case NODE_AV_FILE: case NODE_COND_FILE: case NODE_CLEANUP: return NULL; // Should never happen case NODE_HEADER: return SECTION_NON_ORDERED; // Guaranteed at top by grammar case NODE_AV_RULE: case NODE_XAV_RULE: if (node->data.av_data->flavor == AV_RULE_NEVERALLOW) { // These are somewhat of a unique situation, and the style guide // doesn't mention them explicitly. Maybe they should just group // like other av rules, but they can often have multiple types. // Additionally, the below code assumes that the first string in // the sources is a type or attribute, but in the case of neverallows // it can be "~" return SECTION_NON_ORDERED; } if (node->data.av_data->flavor == AV_RULE_AUDITALLOW) { return SECTION_NON_ORDERED; } if (node->data.av_data->perms && (str_in_sl("associate", node->data.av_data->perms) || str_in_sl("mounton", node->data.av_data->perms))) { return SECTION_NON_ORDERED; // Can be transform or with rules } // The case of multiple source types is weird. For now // just using the first one seems fine. return node->data.av_data->sources->string; case NODE_TT_RULE: // TODO: Are type_member and type_change the same as tt // from an ordering standpoint? // The case of multiple source types is weird. For now // just using the first one seems fine. return node->data.av_data->sources->string; case NODE_RT_RULE: return SECTION_NON_ORDERED; case NODE_ROLE_ALLOW: case NODE_ROLE_TYPES: // These are not in the style guide. I normally see them grouped // with declarations, but maybe a future ordering configuration // can sort them that way return SECTION_NON_ORDERED; case NODE_DECL: case NODE_ALIAS: case NODE_TYPE_ALIAS: case NODE_TYPE_ATTRIBUTE: case NODE_ROLE_ATTRIBUTE: if (is_in_require(node)) { return SECTION_NON_ORDERED; } else { return SECTION_DECLARATION; } case NODE_M4_CALL: case NODE_M4_SIMPLE_MACRO: case NODE_DEFINE: return SECTION_NON_ORDERED; // TODO: It's probably way more complicated than this case NODE_OPTIONAL_POLICY: case NODE_OPTIONAL_ELSE: case NODE_BOOLEAN_POLICY: case NODE_TUNABLE_POLICY: case NODE_IFDEF: case NODE_IFELSE: return get_section(node->first_child); case NODE_M4_ARG: return SECTION_NON_ORDERED; //TODO case NODE_START_BLOCK: if (node->next) { return get_section(node->next); } else { return SECTION_NON_ORDERED; // empty block } case NODE_IF_CALL: // check for filetrans_if first to treat interfaces with the // flags filetrans and transform as _non-ordered if (is_filetrans_if(node->data.ic_data->name)) { return SECTION_NON_ORDERED; } else if (!is_optional(node) && !is_in_ifdef(node) && !is_boolean(node) && !is_tunable(node) && (look_up_in_template_map(node->data.ic_data->name) || is_transform_if(node->data.ic_data->name) || is_role_if(node->data.ic_data->name))) { return SECTION_DECLARATION; } else { if (node->data.ic_data->args) { return node->data.ic_data->args->string; } else { // Empty interface call return SECTION_NON_ORDERED; } } case NODE_TEMP_DEF: case NODE_INTERFACE_DEF: return NULL; // if files only case NODE_REQUIRE: case NODE_GEN_REQ: return SECTION_NON_ORDERED; // Not in style guide case NODE_PERMISSIVE: return SECTION_NON_ORDERED; // Not in style guide case NODE_FC_ENTRY: return NULL; // fc files only case NODE_COMMENT: case NODE_EMPTY: case NODE_SEMICOLON: case NODE_ERROR: return SECTION_NON_ORDERED; default: // Should never happen return NULL; } } void calculate_average_lines(struct section_data *sections) { while (sections) { sections->avg_line = (float)sections->lines_sum / (float)sections->lineno_count; sections = sections->next; } } float get_avg_line_by_name(const char *section_name, const struct section_data *sections) { while (0 != strcmp(sections->section_name, section_name)) { sections = sections->next; if (!sections) { return -1; //Error } } return sections->avg_line; } static bool is_self_rule(const struct policy_node *node) { // Access vector rule with self as target if ((node->flavor == NODE_AV_RULE || node->flavor == NODE_XAV_RULE) && node->data.av_data && node->data.av_data->targets && 0 == strcmp(node->data.av_data->targets->string, "self")) { return true; } // Macro call with self as argument if (node->flavor == NODE_IF_CALL && str_in_sl("self", node->data.ic_data->args) && !look_up_in_ifs_map(node->data.ic_data->name)) { return true; } return false; } static bool is_own_module_rule(const struct policy_node *node, const char *current_mod_name) { if (node->flavor != NODE_AV_RULE && node->flavor != NODE_XAV_RULE && node->flavor != NODE_IF_CALL) { return false; } if (node->flavor == NODE_IF_CALL) { // These should actually be patterns, not real calls if (look_up_in_ifs_map(node->data.ic_data->name)) { return false; } } struct name_list *names = get_names_in_node(node); const struct name_list *cur = names; while (cur) { const char *module_of_type_or_attr = look_up_in_decl_map(cur->data->name, DECL_TYPE); if (!module_of_type_or_attr) { module_of_type_or_attr = look_up_in_decl_map(cur->data->name, DECL_ATTRIBUTE); } if (module_of_type_or_attr && 0 != strcmp(module_of_type_or_attr, current_mod_name)) { free_name_list(names); return false; } cur = cur->next; } free_name_list(names); // This assumes that not found strings are not types from other modules. // This is probably necessary because we'll find strings like "file" or // "read_file_perms" for example. However, in normal mode without context // this could definitely be a problem because we won't find types from // other modules return true; } static bool is_kernel_mod_if_call(const struct policy_node *node) { if (node->flavor != NODE_IF_CALL) { return false; } const char *mod_name = look_up_in_ifs_map(node->data.ic_data->name); if (!mod_name) { return false; } if (0 == strcmp("kernel", mod_name)) { return true; } return false; } static bool is_own_mod_if_call(const struct policy_node *node, const char *current_mod_name) { if (node->flavor != NODE_IF_CALL) { return false; } const char *mod_name = look_up_in_ifs_map(node->data.ic_data->name); if (!mod_name) { return false; } if (current_mod_name && 0 != strcmp(current_mod_name, mod_name)) { return false; } return true; } static bool is_related_mod_if_call(const struct policy_node *node, const char *current_mod_name) { if (node->flavor != NODE_IF_CALL) { return false; } const char *current_mod_prefix = strchr(current_mod_name, '_'); size_t cur_prefix_len; if (current_mod_prefix) { cur_prefix_len = (size_t)(current_mod_prefix - current_mod_name); } else { cur_prefix_len = strlen(current_mod_name); } const char *mod_name = look_up_in_ifs_map(node->data.ic_data->name); if (!mod_name) { return false; } const char *mod_prefix = strchr(mod_name, '_'); size_t prefix_len; if (mod_prefix) { prefix_len = (size_t)(mod_prefix - mod_name); } else { prefix_len = strlen(mod_name); } if (cur_prefix_len == prefix_len && 0 == strncmp(current_mod_name, mod_name, cur_prefix_len)) { return true; } return false; } static bool check_call_layer(const struct policy_node *node, const char *layer_to_check) { if (node->flavor != NODE_IF_CALL) { return false; } const char *mod_name = look_up_in_ifs_map(node->data.ic_data->name); if (!mod_name) { // not an actual interface return false; } const char *layer_name = look_up_in_mod_layers_map(mod_name); if (!layer_name) { return false; } return (0 == strcmp(layer_name, layer_to_check)); } static bool is_kernel_layer_if_call(const struct policy_node *node) { return check_call_layer(node, "kernel"); } static bool is_system_layer_if_call(const struct policy_node *node) { return check_call_layer(node, "system"); } static bool is_optional(const struct policy_node *node) { bool ret = false; while (node) { if (node->flavor == NODE_OPTIONAL_POLICY || node->flavor == NODE_OPTIONAL_ELSE) { ret = true; } else if (node->flavor == NODE_BOOLEAN_POLICY || node->flavor == NODE_TUNABLE_POLICY || node->flavor == NODE_IFDEF || node->flavor == NODE_IFELSE) { ret = false; } node = node->parent; } return ret; } static unsigned optional_depth(const struct policy_node *node) { unsigned ret = 0; while (node) { if (node->flavor == NODE_OPTIONAL_POLICY || node->flavor == NODE_OPTIONAL_ELSE) { ret++; } node = node->parent; } return ret; } static bool is_boolean(const struct policy_node *node) { bool ret = false; while (node) { if (node->flavor == NODE_BOOLEAN_POLICY) { ret = true; } else if (node->flavor == NODE_TUNABLE_POLICY || node->flavor == NODE_OPTIONAL_POLICY || node->flavor == NODE_OPTIONAL_ELSE || node->flavor == NODE_IFDEF || node->flavor == NODE_IFELSE) { ret = false; } node = node->parent; } return ret; } static bool is_tunable(const struct policy_node *node) { bool ret = false; while (node) { if (node->flavor == NODE_TUNABLE_POLICY) { ret = true; } else if (node->flavor == NODE_BOOLEAN_POLICY || node->flavor == NODE_OPTIONAL_POLICY || node->flavor == NODE_OPTIONAL_ELSE || node->flavor == NODE_IFDEF || node->flavor == NODE_IFELSE) { ret = false; } node = node->parent; } return ret; } static bool is_in_ifdef(const struct policy_node *node) { bool ret = false; while (node) { if (node->flavor == NODE_IFDEF || node->flavor == NODE_IFELSE) { ret = true; } else if (node->flavor == NODE_OPTIONAL_POLICY || node->flavor == NODE_OPTIONAL_ELSE || node->flavor == NODE_BOOLEAN_POLICY || node->flavor == NODE_TUNABLE_POLICY) { ret = false; } node = node->parent; } return ret; } enum local_subsection get_local_subsection(const char *mod_name, const struct policy_node *node, enum order_conf variant) { if (!node) { return LSS_UNKNOWN; } if (is_in_ifdef(node)) { return LSS_BUILD_OPTION; } else if (is_optional(node)) { return LSS_OPTIONAL; } else if (is_boolean(node)) { return LSS_BOOLEAN; } else if (is_tunable(node)) { return LSS_TUNABLE; } else if (is_self_rule(node)) { return LSS_SELF; } else if (is_own_module_rule(node, mod_name)) { return LSS_OWN; } else if (is_own_mod_if_call(node, mod_name)) { return LSS_OWN; } else if (variant != ORDER_REF && is_related_mod_if_call(node, mod_name)) { return LSS_RELATED; } else if (is_kernel_mod_if_call(node)) { return LSS_KERNEL_MOD; } else if (variant != ORDER_LIGHT && is_kernel_layer_if_call(node)) { return LSS_KERNEL; } else if (variant != ORDER_LIGHT && is_system_layer_if_call(node)) { return LSS_SYSTEM; } else if (node->flavor == NODE_IF_CALL) { return LSS_OTHER; } else { // TODO conditional, optional etc return LSS_UNKNOWN; } } /* * Treat the following as the same section: * foo_t and foo_r * foo and foo_t * foo and foo_r */ static bool is_same_section(const char *first_section_name, const char *second_section_name) { size_t first_length = strlen(first_section_name); size_t second_length = strlen(second_section_name); if (first_length >= 3 && (first_section_name[first_length-1] == 't' || first_section_name[first_length-1] == 'r') && first_section_name[first_length-2] == '_') { first_length -= 2; } if (second_length >= 3 && (second_section_name[second_length-1] == 't' || second_section_name[second_length-1] == 'r') && second_section_name[second_length-2] == '_') { second_length -= 2; } return (0 == strncmp(first_section_name, second_section_name, first_length > second_length ? first_length : second_length)); } #define CHECK_ORDERING(to_check_first, to_check_second, comp, ret) \ if (to_check_first == comp) { \ return ret; \ } \ if (to_check_second == comp) { \ return -ret; \ } \ // Call this in order of an ordering on enums. It returns a positive or // negative value based on which one it encounters first. #define CHECK_FLAVOR_ORDERING(data_flavor, comp, ret) \ CHECK_ORDERING(first->data.data_flavor->flavor, second->data.data_flavor->flavor, comp, ret) enum order_difference_reason compare_nodes_refpolicy_generic(const struct ordering_metadata *ordering_data, const struct policy_node *first, const struct policy_node *second, enum order_conf variant) { const char *first_section_name = get_section(first); const char *second_section_name = get_section(second); if (first_section_name == NULL || second_section_name == NULL) { return ORDERING_ERROR; } if (0 == strcmp(first_section_name, SECTION_NON_ORDERED) || 0 == strcmp(second_section_name, SECTION_NON_ORDERED)) { return ORDER_EQUAL; } if (!is_same_section(first_section_name, second_section_name)) { if (0 != strcmp(first_section_name, SECTION_DECLARATION) && (0 == strcmp(second_section_name, SECTION_DECLARATION) || (get_avg_line_by_name(first_section_name, ordering_data->sections) > get_avg_line_by_name(second_section_name, ordering_data->sections)))) { return -ORDER_SECTION; } else { return ORDER_SECTION; } } // If we made it to this point the two nodes are in the same section if (0 == strcmp(first_section_name, SECTION_DECLARATION)) { if (first->flavor == NODE_DECL && second->flavor == NODE_DECL) { if (first->data.d_data->flavor != second->data.d_data->flavor) { CHECK_FLAVOR_ORDERING(d_data, DECL_BOOL, ORDER_DECLARATION_SUBSECTION); CHECK_FLAVOR_ORDERING(d_data, DECL_ATTRIBUTE, ORDER_DECLARATION_SUBSECTION); // Types and roles should intersperse } else { // TODO: same subsection } } return ORDER_EQUAL; } // Local policy rules sections const enum local_subsection lss_first = get_local_subsection(ordering_data->mod_name, first, variant); const enum local_subsection lss_second = get_local_subsection(ordering_data->mod_name, second, variant); if (lss_first == LSS_UNKNOWN || lss_second == LSS_UNKNOWN) { return ORDER_EQUAL; // ... Maybe? Should this case be handled earlier? } if (lss_first != lss_second) { CHECK_ORDERING(lss_first, lss_second, LSS_SELF, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_OWN, ORDER_LOCAL_SUBSECTION); if (variant == ORDER_REF) { CHECK_ORDERING(lss_first, lss_second, LSS_KERNEL_MOD, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_KERNEL, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_SYSTEM, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_OTHER, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_BUILD_OPTION, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_BOOLEAN, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_TUNABLE, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_OPTIONAL, ORDER_LOCAL_SUBSECTION); } else if (variant == ORDER_LIGHT) { CHECK_ORDERING(lss_first, lss_second, LSS_RELATED, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_KERNEL_MOD, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_OTHER, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_BUILD_OPTION, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_BOOLEAN, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_TUNABLE, ORDER_LOCAL_SUBSECTION); CHECK_ORDERING(lss_first, lss_second, LSS_OPTIONAL, ORDER_LOCAL_SUBSECTION); } } if (variant == ORDER_REF || variant == ORDER_LIGHT) { // alphabetical top-level interfaces if (lss_first != LSS_BUILD_OPTION && lss_first != LSS_BOOLEAN && lss_first != LSS_TUNABLE && (lss_first != LSS_OPTIONAL || // sort optionals only based on first node with same depth (lss_first == lss_second && first->prev->flavor == NODE_START_BLOCK && second->prev->flavor == NODE_START_BLOCK && optional_depth(first) == optional_depth(second))) && first->flavor == NODE_IF_CALL && second->flavor == NODE_IF_CALL) { const char *first_mod_name = look_up_in_ifs_map(first->data.ic_data->name); const char *second_mod_name = look_up_in_ifs_map(second->data.ic_data->name); if (first_mod_name && second_mod_name) { const int compare = strcmp(first_mod_name, second_mod_name); if (compare < 0) { return ORDER_ALPHABETICAL; } else if (compare > 0) { return -ORDER_ALPHABETICAL; } } } // alphabetical optionals // sort by the first node in a optional block if (lss_first == LSS_OPTIONAL && lss_second == LSS_OPTIONAL && first->first_child && first->first_child->next && second->first_child && second->first_child->next) { return compare_nodes_refpolicy_generic(ordering_data, first->first_child->next, second->first_child->next, variant); } } return ORDER_EQUAL; } enum order_difference_reason compare_nodes_refpolicy(const struct ordering_metadata *ordering_data, const struct policy_node *first, const struct policy_node *second) { return compare_nodes_refpolicy_generic(ordering_data, first, second, ORDER_REF); } enum order_difference_reason compare_nodes_refpolicy_light(const struct ordering_metadata *ordering_data, const struct policy_node *first, const struct policy_node *second) { return compare_nodes_refpolicy_generic(ordering_data, first, second, ORDER_LIGHT); } enum order_difference_reason compare_nodes_refpolicy_lax(const struct ordering_metadata *ordering_data, const struct policy_node *first, const struct policy_node *second) { return compare_nodes_refpolicy_generic(ordering_data, first, second, ORDER_LAX); } const char *lss_to_string(enum local_subsection lss) { switch (lss) { case LSS_SELF: return "self rule"; case LSS_OWN: return "own module rule"; case LSS_RELATED: return "related module interface"; case LSS_KERNEL_MOD: return "kernel module interface"; case LSS_KERNEL: return "kernel layer interface"; case LSS_SYSTEM: return "system layer interface"; case LSS_OTHER: return "general interface"; case LSS_BUILD_OPTION: return "build option"; case LSS_BOOLEAN: return "boolean policy block"; case LSS_TUNABLE: return "tunable policy block"; case LSS_OPTIONAL: return "optional policy block"; case LSS_UNKNOWN: default: return "unknown subsection"; } } char *get_ordering_reason(const struct ordering_metadata *order_data, unsigned int index, enum order_conf variant) { unsigned int distance = 1; unsigned int nearest_index = 0; enum order_difference_reason reason = ORDER_EQUAL; while (nearest_index == 0) { if (distance < index && order_data->nodes[index-distance].in_order) { reason = compare_nodes_refpolicy_generic(order_data, order_data->nodes[index-distance].node, order_data->nodes[index].node, variant); if (reason < 0) { nearest_index = index - distance; break; } } if (index + distance < order_data->order_node_len && order_data->nodes[index+distance].in_order) { reason = compare_nodes_refpolicy_generic(order_data, order_data->nodes[index].node, order_data->nodes[index+distance].node, variant); if (reason < 0) { nearest_index = index + distance; break; } } distance++; if ((distance > index) && (index + distance > order_data->order_node_len)) { return NULL; // Error } } const struct policy_node *this_node = order_data->nodes[index].node; const struct policy_node *other_node = order_data->nodes[nearest_index].node; const char *before_after; if (nearest_index > index) { before_after = "before"; } else { before_after = "after"; } const char *reason_str; char *followup_str = NULL; enum local_subsection other_lss; const char *node_section; const char *other_section; switch (-reason) { case ORDER_EQUAL: return NULL; // Error case ORDER_SECTION: node_section = get_section(this_node); other_section = get_section(other_node); if (!node_section || !other_section) { return NULL; // Error } if (0 == strcmp(SECTION_DECLARATION, node_section)) { // This is the first section reason_str = "that is not a declaration"; } else if (0 == strcmp(SECTION_DECLARATION, other_section)) { // The other section is the first section if (other_node->flavor == NODE_IF_CALL && is_transform_if(other_node->data.ic_data->name)) { reason_str = "that is a transform interface"; } else { reason_str = "that is a declaration"; } } else { reason_str = "that is in a different section"; } int r = asprintf(&followup_str, " (This node is in the section for %s rules and the other is in the section for %s rules.)", node_section, other_section); if (r == -1) { return NULL; //ERROR } break; case ORDER_DECLARATION_SUBSECTION: reason_str = "that is associated with a different sort of declaration"; break; case ORDER_LOCAL_SUBSECTION: other_lss = get_local_subsection(order_data->mod_name, other_node, variant); switch (other_lss) { case LSS_SELF: reason_str = "that is a self rule"; break; case LSS_OWN: reason_str = "that refers to types owned by this module"; break; case LSS_RELATED: reason_str = "that calls an interface located in a related module"; break; case LSS_KERNEL_MOD: reason_str = "that calls an interface located in the kernel module"; break; case LSS_KERNEL: reason_str = "that calls an interface located in the kernel layer"; break; case LSS_SYSTEM: reason_str = "that calls an interface located in the system layer"; break; case LSS_OTHER: reason_str = variant == ORDER_REF ? "that calls an interface not located in the kernel or system layer" : "that calls a non-optional interface"; break; case LSS_BUILD_OPTION: reason_str = "that is controlled by a build option"; break; case LSS_BOOLEAN: reason_str = "that is in a boolean block"; break; case LSS_TUNABLE: reason_str = "that is in a tunable block"; break; case LSS_OPTIONAL: reason_str = "that is in an optional block"; break; case LSS_UNKNOWN: return NULL; //Error default: //Shouldn't happen return NULL; } if (other_lss == LSS_KERNEL || other_lss == LSS_SYSTEM || other_lss == LSS_OTHER) { enum local_subsection this_lss = get_local_subsection(order_data->mod_name, this_node, variant); if (this_lss == LSS_KERNEL) { followup_str = xstrdup(" (This interface is in the kernel layer.)"); } else if (this_lss == LSS_SYSTEM) { followup_str = xstrdup(" (This interface is in the system layer.)"); } else if (this_lss == LSS_OTHER) { followup_str = xstrdup(" (This interface is in a layer other than kernel or system.)"); } else if (this_lss == LSS_KERNEL_MOD) { followup_str = xstrdup(" (This interface is in the kernel module.)"); } // Otherwise, it's not an interface call and is hopefully obvious to the user what layer its in } break; case ORDER_ALPHABETICAL: if (nearest_index > index) { reason_str = "that is alphabetically earlier"; } else { reason_str = "that is alphabetically later"; } break; case ORDERING_ERROR: return NULL; default: //Shouldn't happen return NULL; } size_t str_len = strlen(reason_str) + strlen(before_after) + strlen("Line out of order. It is of type ") + strlen(lss_to_string(get_local_subsection(order_data->mod_name, this_node, variant))) + 1 + strlen(" line ") + 13; // 13 is enough for the maximum // length of an unsigned int (10) // plus a final period, a space // and a null terminator if (followup_str) { str_len += strlen(followup_str); } char *ret = xmalloc(sizeof(char) * str_len); ssize_t written = snprintf(ret, str_len, "Line out of order. It is of type %s %s line %u %s.", lss_to_string(get_local_subsection(order_data->mod_name, this_node, variant)), before_after, other_node->lineno, reason_str); if (written < 0) { free(followup_str); free(ret); return NULL; } if (followup_str) { strncat(ret, followup_str, str_len - (size_t)written); } free(followup_str); return ret; } void free_ordering_metadata(struct ordering_metadata *to_free) { if (to_free == NULL) { return; } free_section_data(to_free->sections); free(to_free); } void free_section_data(struct section_data *to_free) { if (to_free == NULL) { return; } free(to_free->section_name); free_section_data(to_free->next); free(to_free); } selint-1.5.1/src/ordering.h000066400000000000000000000227111475050262500155760ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ORDERING_H #define ORDERING_H #include #include "selint_error.h" #include "tree.h" #include "check_hooks.h" enum order_difference_reason { ORDER_EQUAL =0, ORDER_SECTION=1, ORDER_DECLARATION_SUBSECTION, ORDER_LOCAL_SUBSECTION, ORDER_ALPHABETICAL, ORDERING_ERROR=-1024, // Since we will be negating values of this enum // we want it to have a signed internal // representation. The compiler gets to choose which // so by having a negative value explicitly declared // we force it to be signed }; enum local_subsection { LSS_SELF, LSS_OWN, LSS_RELATED, LSS_KERNEL_MOD, LSS_KERNEL, LSS_SYSTEM, LSS_OTHER, LSS_BUILD_OPTION, LSS_BOOLEAN, LSS_TUNABLE, LSS_OPTIONAL, LSS_UNKNOWN, }; struct section_data { char *section_name; // The name of the section this section_data // node contains rules for. This can be either // the name of a type, or a special name beginning // with _. Special names are "_declarations" for // the declarations section at the top and // "_non_ordered" for nodes that should be ignored // in ordering unsigned int lineno_count; unsigned int lines_sum; float avg_line; struct section_data *next; }; struct order_node { const struct policy_node *node; int seq_prev; // The index of previous node in the sequence // or -1 if this is the first node in the sequence int end_of_seq; // This is the index of the smallest value // of an end of a sequence of length i+1, where // i is the index of this node in the array unsigned int in_order; }; struct ordering_metadata { const char *mod_name; struct section_data *sections; size_t order_node_len; struct order_node nodes[]; }; /********************************** * Allocate and initialize an ordering_metadata for the structure. * Calculate the sections and order them. * This function allocates memory for, but does not populate the * nodes[] array. That will be populated on the next pass in the * calculate_longest_increasing_subsequence function. * data (in) - The metadata about the file being scanned * head (in) - Pointer to the file node at the top of the AST * for a file. * * Returns - A new ordering_metadata structure with all memory allocated * and all data except the nodesp[ array populated. The caller is * responsible for freeing all memory. Returns NULL on error **********************************/ struct ordering_metadata *prepare_ordering_metadata(const struct check_data *data, const struct policy_node *head); /********************************** * Calculate the longest increasing subsequence in a given file * Misordered nodes are nodes that are not in this sequence. * Updates the ordering_metadata structure to mark the in order and * out of order nodes in the nodes[] array. * head (in) - Pointer to the file node at the top of the AST * for a file. * ordering (in/out) - A structure of metadata that has been generated * by a call to prepare_ordering_metadata for use in the ordering * comp_func (in) - A function to call for comparison of nodes. It should * return a positive value if the second node should go after the first, * a negative value is the second node should go before the first, and 0 * if the two nodes can go in any relative order. **********************************/ void calculate_longest_increasing_subsequence(const struct policy_node *head, struct ordering_metadata *ordering, enum order_difference_reason (*comp_func)(const struct ordering_metadata *order_data, const struct policy_node *first, const struct policy_node *second)); /********************************** * Add information about a line on lineno in section section_name * to the list of sections **********************************/ enum selint_error add_section_info(struct section_data *sections, const char *section_name, unsigned int lineno); /********************************** * Get the section name for a particular policy node. This is typically * the source type for most node varieties. For the declarations section * at the top it is "_declaration" **********************************/ const char *get_section(const struct policy_node *node); /********************************** * Run through all sections in the section_data linked list and set * the average line number variable for each based on the sum and * count of line numbers **********************************/ void calculate_average_lines(struct section_data *sections); /********************************** * Get the average line number of a section, based on the section name **********************************/ float get_avg_line_by_name(const char *section_name, const struct section_data *sections); /********************************** * Get the subsection within the rules for a domain for a particular policy node **********************************/ enum local_subsection get_local_subsection(const char *mod_name, const struct policy_node *node, enum order_conf variant); /********************************** * Compare two nodes according to the refpolicy ordering conventions * located at https://github.com/SELinuxProject/refpolicy/wiki/StyleGuide * Variant is the config option for how strictly to enforce the style guide. * ORDER_REF - enforce the style guide as written * ORDER_LIGHT - enforce the style guide with the following exceptions: * - No distinction between kernel and system layer, * just kernel module -> non-optional -> optional * ORDER_LAX - enforce the style guide with the following exceptions: * - No ordering restrictions are enforced on the relative ordering * of interface calls and blocks * Return a positive value if the second node should go after the first, * a negative value is the second node should go before the first and * zero if they can go in either order. **********************************/ enum order_difference_reason compare_nodes_refpolicy_generic(const struct ordering_metadata *ordering_data, const struct policy_node *first, const struct policy_node *second, enum order_conf variant); /********************************** * Wrapper for compare_nodes_refpolicy_generic for refpolicy ordering **********************************/ enum order_difference_reason compare_nodes_refpolicy(const struct ordering_metadata *ordering_data, const struct policy_node *first, const struct policy_node *second); /********************************** * Wrapper for compare_nodes_refpolicy_generic for refpolicy-light ordering **********************************/ enum order_difference_reason compare_nodes_refpolicy_light(const struct ordering_metadata *ordering_data, const struct policy_node *first, const struct policy_node *second); /********************************** * Wrapper for compare_nodes_refpolicy_generic for refpolicy-lax ordering **********************************/ enum order_difference_reason compare_nodes_refpolicy_lax(const struct ordering_metadata *ordering_data, const struct policy_node *first, const struct policy_node *second); /********************************** * Get a string describing the local subsection. * The strings for kernel and system will be inserted * into a description by get_ordering_reason, so if they * are modified, that code should be modified as well. **********************************/ const char *lss_to_string(enum local_subsection lss); /********************************** * Get a string explaining why a node is out of order. * This is done by looking for the nearest node that is globally * in order and relatively out of order with this node and checking * what the reason for their out of order comparison is **********************************/ char *get_ordering_reason(const struct ordering_metadata *order_data, unsigned int index, enum order_conf variant); void free_ordering_metadata(struct ordering_metadata *to_free); void free_section_data(struct section_data *to_free); #endif selint-1.5.1/src/parse.y000066400000000000000000000755231475050262500151310ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ %define parse.error verbose %locations %define api.pure full %lex-param {yyscan_t scanner} %parse-param {yyscan_t scanner} %code requires { typedef void* yyscan_t; } %{ #include #include #include #include #include "tree.h" #include "parse_functions.h" #include "check_hooks.h" #include "util.h" #include "color.h" #include "xalloc.h" #define YYDEBUG 1 #define YYMALLOC xmalloc #define YYREALLOC xrealloc struct location { unsigned int first_line; unsigned int first_column; unsigned int last_line; unsigned int last_column; }; #define YYLTYPE struct location %} %union { char *string; char symbol; struct string_list *sl; enum av_rule_flavor av_flavor; enum node_flavor node_flavor; } %{ // local variables and functions static const char *parsing_filename; static struct policy_node *cur; static enum node_flavor expected_node_flavor; static void yyerror(const YYLTYPE *locp, yyscan_t yyscanner, char const *msg); // lexer extern void yyrestart(FILE *input_file , yyscan_t yyscanner); extern int yylex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param, yyscan_t yyscanner); extern int yylex_init(yyscan_t* scanner); extern int yylex_destroy(yyscan_t scanner); extern char *current_lines[LINES_TO_CACHE]; extern unsigned line_cache_index; extern void reset_current_lines(void); %} %code provides { // number of lines stored, printed on parse errors for multiline statements #define LINES_TO_CACHE 5 // global prototype struct policy_node *yyparse_wrapper(FILE *filefd, const char *filename, enum node_flavor expected_flavor); } %token STRING; %token NUM_STRING; %token IPV4; %token IPV4_CIDR; %token IPV6; %token IPV6_CIDR; %token NUMBER; %token QUOTED_STRING; %token VERSION_NO; %token SELINT_COMMAND; %token UNKNOWN_TOKEN; %token POLICY_MODULE; %token MODULE; %token TYPE; %token TYPEALIAS; %token ALIAS; %token ATTRIBUTE; %token BOOL; %token TYPE_ATTRIBUTE; %token ROLE_ATTRIBUTE; %token ROLE; %token TYPES; %token ATTRIBUTE_ROLE; %token ALLOW; %token ALLOW_XPERM; %token AUDIT_ALLOW; %token AUDIT_ALLOW_XPERM; %token DONT_AUDIT; %token DONT_AUDIT_XPERM; %token NEVER_ALLOW; %token NEVER_ALLOW_XPERM; %token TYPE_TRANSITION; %token TYPE_MEMBER; %token TYPE_CHANGE; %token RANGE_TRANSITION; %token ROLE_TRANSITION; %token OPTIONAL_POLICY; %token GEN_REQUIRE; %token GEN_BOOL; %token GEN_TUNABLE; %token REQUIRE; %token TUNABLE_POLICY; %token IFELSE; %token REFPOLICYWARN; %token CLASS; %token COMMON; %token INHERITS; %token IFDEF; %token IFNDEF; %token IF; %token ELSE; %token GENFSCON; %token SID; %token PORTCON; %token NETIFCON; %token NODECON; %token FS_USE_TRANS; %token FS_USE_XATTR; %token FS_USE_TASK; %token DEFINE; %token GEN_USER; %token GEN_CONTEXT; %token PERMISSIVE; %token TYPEBOUNDS; %token INTERFACE; %token TEMPLATE; %token USERDEBUG_OR_ENG; %token FILE_TYPE_SPECIFIER; %token OPEN_PAREN; %token COMMA; %token PERIOD; %token CLOSE_PAREN; %token OPEN_CURLY; %token CLOSE_CURLY; %token COLON; %token SEMICOLON; %token BACKTICK; %token SINGLE_QUOTE; %token TILDE; %token STAR; %token DASH; %left AND; %left OR; %left XOR; %right NOT; %left EQUAL; %left NOT_EQUAL; %token COMMENT; %type string_list %type comma_string_list %type strings %type xperm_list %type xperm_items %type spt_contents %type spt_content %type string_or_quoted_string %type sl_item %type xperm_item %type arg_list %type arg_list_items %type arg_list_item %type arg %type args %type arg_m4_quoted %type mls_range %type mls_level %type mls_component %type maybe_string_comma %type maybe_selint_disable %type av_type %type xperm_av_type %type if_keyword %destructor { free($$); } %destructor { free_string_list($$); } %% selinux_file: %empty /* empty */ { cur->flavor = NODE_EMPTY; // avoid variable set but not used warning from Clang 15 in bison generated code (void)yynerrs; } | te_policy | comments te_policy | if_file | comments if_file | spt_file | comments spt_file | av_file | comments av_file | global_conditions_file | comments global_conditions_file | comments ; // TE File parsing te_policy: header body ; comments: comment | comment comments ; comment: COMMENT { insert_comment(&cur, @$.first_line); } ; maybe_selint_disable: SELINT_COMMAND | %empty { $$ = NULL; } ; header: POLICY_MODULE OPEN_PAREN STRING maybe_header_version CLOSE_PAREN { if (expected_node_flavor != NODE_TE_FILE) { free($3); const struct location loc = { @1.first_line, @1.first_column, @5.last_line, @5.last_column }; yyerror(&loc, NULL, "Error: Unexpected te-file parsed"); YYERROR; } insert_header(&cur, $3, HEADER_MACRO, @$.first_line); free($3); } // Version number isn't needed | MODULE STRING header_version SEMICOLON { if (expected_node_flavor != NODE_TE_FILE) { free($2); const struct location loc = { @1.first_line, @1.first_column, @4.last_line, @4.last_column }; yyerror(&loc, NULL, "Error: Unexpected te-file parsed"); YYERROR; } insert_header(&cur, $2, HEADER_BARE, @$.first_line); free($2); } ; header_version: VERSION_NO { free($1); } | NUMBER { free($1); } ; maybe_header_version: COMMA header_version | %empty ; body: lines | %empty ; lines: lines line | line ; line: bare_line maybe_selint_disable { save_command(cur, $2); free($2); } ; bare_line: declaration | type_attribute | role_attribute | type_alias | rule | xperm_rule | role_allow | role_types | type_transition | range_transition | role_transition | interface_call | optional_block | require | m4_call | m4_simple_macro | cond_expr | genfscon | sid | portcon | netifcon | nodecon | fs_use | define | gen_user | permissive | typebounds | SEMICOLON { insert_semicolon(&cur, @$.first_line); } | COMMENT // Would like to do error recovery, but the best strategy seems to be to skip // to next newline, which lex doesn't give us right now. // Also, we would need to know in yyerror whether the error was recoverable | error { const struct location loc = { @1.first_line, @1.first_column, @1.last_line, @1.last_column }; yyerror(&loc, NULL, "Error: Invalid statement"); YYABORT; } ; declaration: type_declaration | ATTRIBUTE STRING SEMICOLON { insert_declaration(&cur, DECL_ATTRIBUTE, $2, NULL, @$.first_line); free($2); } | CLASS STRING string_list SEMICOLON { insert_declaration(&cur, DECL_CLASS, $2, $3, @$.first_line); free($2); } | ROLE STRING SEMICOLON { insert_declaration(&cur, DECL_ROLE, $2, NULL, @$.first_line); free($2); } | ATTRIBUTE_ROLE STRING SEMICOLON { insert_declaration(&cur, DECL_ATTRIBUTE_ROLE, $2, NULL, @$.first_line); free($2); } | bool_declaration ; type_declaration: TYPE STRING SEMICOLON { insert_declaration(&cur, DECL_TYPE, $2, NULL, @$.first_line); free($2); } | TYPE STRING COMMA comma_string_list SEMICOLON { insert_declaration(&cur, DECL_TYPE, $2, $4, @$.first_line); free($2); } | TYPE STRING ALIAS string_list SEMICOLON { insert_declaration(&cur, DECL_TYPE, $2, NULL, @$.first_line); free($2); insert_aliases(&cur, $4, DECL_TYPE, @$.first_line); } | TYPE STRING ALIAS string_list COMMA comma_string_list SEMICOLON { insert_declaration(&cur, DECL_TYPE, $2, $6, @$.first_line); free($2); insert_aliases(&cur, $4, DECL_TYPE, @$.first_line); } ; bool_declaration: BOOL STRING SEMICOLON { insert_declaration(&cur, DECL_BOOL, $2, NULL, @$.first_line); free($2); } | GEN_BOOL OPEN_PAREN STRING COMMA STRING CLOSE_PAREN { insert_declaration(&cur, DECL_BOOL, $3, NULL, @$.first_line); free($3); free($5); } | GEN_TUNABLE OPEN_PAREN BACKTICK STRING SINGLE_QUOTE COMMA STRING CLOSE_PAREN { insert_declaration(&cur, DECL_BOOL, $4, NULL, @$.first_line); free($4); free($7); } | GEN_TUNABLE OPEN_PAREN STRING COMMA STRING CLOSE_PAREN { insert_declaration(&cur, DECL_BOOL, $3, NULL, @$.first_line); free($3); free($5); } ; type_alias: TYPEALIAS STRING ALIAS string_list SEMICOLON { insert_type_alias(&cur, $2, @$.first_line); insert_aliases(&cur, $4, DECL_TYPE, @$.first_line); free($2); } ; type_attribute: TYPE_ATTRIBUTE STRING comma_string_list SEMICOLON { insert_type_attribute(&cur, $2, $3, @$.first_line); free($2); } ; role_attribute: ROLE_ATTRIBUTE STRING comma_string_list SEMICOLON { insert_role_attribute(&cur, $2, $3, @$.first_line); free($2); } rule: av_type string_list string_list COLON string_list string_list SEMICOLON { insert_av_rule(&cur, $1, $2, $3, $5, $6, @$.first_line); } ; av_type: ALLOW { $$ = AV_RULE_ALLOW; } | AUDIT_ALLOW { $$ = AV_RULE_AUDITALLOW; } | DONT_AUDIT { $$ = AV_RULE_DONTAUDIT; } | NEVER_ALLOW { $$ = AV_RULE_NEVERALLOW; } ; xperm_rule: xperm_av_type string_list string_list COLON string_list STRING xperm_list SEMICOLON { insert_xperm_av_rule(&cur, $1, $2, $3, $5, $6, $7, @$.first_line); free($6); } ; xperm_av_type: ALLOW_XPERM { $$ = AV_RULE_ALLOW; } | AUDIT_ALLOW_XPERM { $$ = AV_RULE_AUDITALLOW; } | DONT_AUDIT_XPERM { $$ = AV_RULE_DONTAUDIT; } | NEVER_ALLOW_XPERM { $$ = AV_RULE_NEVERALLOW; } ; xperm_list: OPEN_CURLY xperm_items CLOSE_CURLY { $$ = $2; } | TILDE xperm_list { $$ = sl_from_str("~"); $$->next = $2; } | xperm_item { $$ = sl_from_str_consume($1); } ; xperm_items: xperm_items xperm_item { $$ = concat_string_lists($1, sl_from_str_consume($2)); } | xperm_item { $$ = sl_from_str_consume($1); } | xperm_item DASH xperm_item { $$ = concat_string_lists(sl_from_str_consume($1), concat_string_lists(sl_from_str("-"), sl_from_str_consume($3))); } // TODO: validate usage: enforce two surrounding increasing elements ; xperm_item: STRING | NUM_STRING | NUMBER ; string_list: OPEN_CURLY strings CLOSE_CURLY { $$ = $2; } | TILDE string_list { $$ = sl_from_str("~"); $$->next = $2; } | sl_item { $$ = sl_from_str_consume($1); } | STAR { $$ = sl_from_str("*"); } ; strings: strings sl_item { $$ = concat_string_lists($1, sl_from_str_consume($2)); } | sl_item { $$ = sl_from_str_consume($1); } ; string_or_quoted_string: STRING | QUOTED_STRING ; sl_item: string_or_quoted_string | DASH STRING { $$ = xmalloc(sizeof(char) * (strlen($2) + 2)); $$[0] = '-'; $$[1] = '\0'; strcat($$, $2); free($2);} ; comma_string_list: comma_string_list COMMA STRING { $$ = concat_string_lists($1, sl_from_str_consume($3)); } | STRING { $$ = sl_from_str_consume($1); } ; role_allow: // It is an error for av_type to be anything other than ALLOW, but specifying ALLOW here is // a grammar conflict, so we leave it general in the parse rule and then check av_type string_list string_list SEMICOLON { if ($1 != AV_RULE_ALLOW) { free_string_list($2); free_string_list($3); const struct location loc = { @1.first_line, @1.first_column, @4.last_line, @4.last_column }; yyerror(&loc, NULL, "Incomplete AV rule"); YYERROR; } insert_role_allow(&cur, $2, $3, @$.first_line); } ; role_types: ROLE STRING TYPES string_list SEMICOLON { insert_role_types(&cur, $2, $4, @$.first_line); free($2); } ; type_transition: TYPE_TRANSITION string_list string_list COLON string_list STRING SEMICOLON { insert_type_transition(&cur, TT_TT, $2, $3, $5, $6, NULL, @$.first_line); free($6); } | TYPE_TRANSITION string_list string_list COLON string_list STRING QUOTED_STRING SEMICOLON { insert_type_transition(&cur, TT_TT, $2, $3, $5, $6, $7, @$.first_line); free($6); free($7); } | TYPE_MEMBER string_list string_list COLON string_list STRING SEMICOLON { insert_type_transition(&cur, TT_TM, $2, $3, $5, $6, NULL, @$.first_line); free($6); } | TYPE_CHANGE string_list string_list COLON string_list STRING SEMICOLON { insert_type_transition(&cur, TT_TC, $2, $3, $5, $6, NULL, @$.first_line); free($6); } ; range_transition: RANGE_TRANSITION string_list string_list COLON string_list mls_range SEMICOLON { insert_type_transition(&cur, TT_RT, $2, $3, $5, $6, NULL, @$.first_line); free($6); } ; role_transition: ROLE_TRANSITION string_list string_list STRING SEMICOLON { insert_role_transition(&cur, $2, $3, NULL, $4, @$.first_line); free($4); } | ROLE_TRANSITION string_list string_list COLON string_list STRING SEMICOLON { insert_role_transition(&cur, $2, $3, $5, $6, @$.first_line); free($6); } ; interface_call: STRING OPEN_PAREN args CLOSE_PAREN { insert_interface_call(&cur, $1, $3, @$.first_line); free($1); } | STRING OPEN_PAREN CLOSE_PAREN { insert_interface_call(&cur, $1, NULL, @$.first_line); free($1); } ; optional_block: optional_open lines SINGLE_QUOTE CLOSE_PAREN { end_optional_policy(&cur); } | optional_open SINGLE_QUOTE CLOSE_PAREN { end_optional_policy(&cur); } | optional_open lines SINGLE_QUOTE COMMA { end_optional_policy(&cur); } BACKTICK { begin_optional_else(&cur, @$.first_line); } lines SINGLE_QUOTE CLOSE_PAREN { end_optional_else(&cur); } ; optional_open: OPTIONAL_POLICY OPEN_PAREN BACKTICK maybe_selint_disable { begin_optional_policy(&cur, @$.first_line); save_command(cur->parent, $4); free($4); } ; require: gen_require_begin BACKTICK maybe_selint_disable require_lines SINGLE_QUOTE CLOSE_PAREN { end_gen_require(&cur, 0); save_command(cur, $3); free($3); } | gen_require_begin require_lines CLOSE_PAREN { end_gen_require(&cur, 1); } | REQUIRE OPEN_CURLY maybe_selint_disable { begin_require(&cur, @$.first_line); save_command(cur->parent, $3); free($3); } require_lines CLOSE_CURLY { end_require(&cur); } ; gen_require_begin: GEN_REQUIRE OPEN_PAREN maybe_selint_disable { begin_gen_require(&cur, @$.first_line); save_command(cur->parent, $3); free($3); } ; require_lines: require_lines require_line | require_line ; require_line: TYPE comma_string_list SEMICOLON maybe_selint_disable { for (const struct string_list *iter = $2; iter; iter = iter->next) { insert_declaration(&cur, DECL_TYPE, iter->string, NULL, @$.first_line); save_command(cur, $4); } free_string_list($2); free($4); } | ATTRIBUTE comma_string_list SEMICOLON maybe_selint_disable { for (const struct string_list *iter = $2; iter; iter = iter->next) { insert_declaration(&cur, DECL_ATTRIBUTE, iter->string, NULL, @$.first_line); save_command(cur, $4); } free_string_list($2); free($4); } | ROLE comma_string_list SEMICOLON maybe_selint_disable { for (const struct string_list *iter = $2; iter; iter = iter->next) { insert_declaration(&cur, DECL_ROLE, iter->string, NULL, @$.first_line); save_command(cur, $4); } free_string_list($2); free($4); } | ATTRIBUTE_ROLE comma_string_list SEMICOLON maybe_selint_disable { for (const struct string_list *iter = $2; iter; iter = iter->next) { insert_declaration(&cur, DECL_ATTRIBUTE_ROLE, iter->string, NULL, @$.first_line); save_command(cur, $4); } free_string_list($2); free($4); } | BOOL comma_string_list SEMICOLON maybe_selint_disable { for (const struct string_list *iter = $2; iter; iter = iter->next) { insert_declaration(&cur, DECL_BOOL, iter->string, NULL, @$.first_line); save_command(cur, $4); } free_string_list($2); free($4); } | CLASS STRING string_list SEMICOLON maybe_selint_disable { insert_declaration(&cur, DECL_CLASS, $2, $3, @$.first_line); save_command(cur, $5); free($2); free($5); } | ifdef_opener BACKTICK require_lines SINGLE_QUOTE CLOSE_PAREN { end_ifdef(&cur);} | ifdef_opener require_lines CLOSE_PAREN { end_ifdef(&cur);} | m4_simple_macro | COMMENT ; m4_simple_macro: STRING { insert_m4simplemacro(&cur, $1, @$.first_line); } ; m4_call: ifdef | ifelse | refpolicywarn | userdebug_or_eng ; ifdef_opener: if_or_ifn OPEN_PAREN BACKTICK STRING SINGLE_QUOTE COMMA { begin_ifdef(&cur, @$.first_line); free($4); } ; ifdef: ifdef_opener m4_args CLOSE_PAREN { end_ifdef(&cur);} ; if_or_ifn: IFDEF | IFNDEF ; ifelse: IFELSE OPEN_PAREN { begin_ifelse(&cur, @$.first_line); } m4_args CLOSE_PAREN { end_ifelse(&cur); } ; refpolicywarn: REFPOLICYWARN OPEN_PAREN BACKTICK arbitrary_m4_string SINGLE_QUOTE CLOSE_PAREN ; userdebug_or_eng: USERDEBUG_OR_ENG OPEN_PAREN BACKTICK lines SINGLE_QUOTE CLOSE_PAREN ; arbitrary_m4_string: m4_string_elem | m4_string_elem arbitrary_m4_string ; m4_string_elem: STRING { free($1); } | OPEN_PAREN | CLOSE_PAREN | OPEN_CURLY | CLOSE_CURLY | COMMA | PERIOD | COLON | SEMICOLON | DASH | av_type | OPTIONAL_POLICY | IFDEF | GEN_REQUIRE | COMMENT | BACKTICK arbitrary_m4_string SINGLE_QUOTE ; condition: STRING { save_identifier(cur->parent, $1); } | NOT condition | condition AND condition | condition OR condition | condition XOR condition | condition EQUAL condition | condition NOT_EQUAL condition | OPEN_PAREN condition CLOSE_PAREN ; m4_args: { begin_m4_argument(&cur, @$.first_line); } m4_argument { end_m4_argument(&cur); } | m4_args COMMA { begin_m4_argument(&cur, @$.first_line); } m4_argument { end_m4_argument(&cur); } ; m4_argument: %empty | BACKTICK SINGLE_QUOTE | BACKTICK lines SINGLE_QUOTE | STRING { free($1); } ; arg_list: OPEN_CURLY arg_list_items CLOSE_CURLY { $$ = $2; } | TILDE arg_list { $$ = sl_from_str("~"); $$->next = $2; } | arg_list_item { $$ = sl_from_str_consume($1); } ; arg_list_items: arg_list_items arg_list_item { $$ = concat_string_lists($1, sl_from_str_consume($2)); } | arg_list_item { $$ = sl_from_str_consume($1); } ; arg_list_item: DASH arg_list_item { $$ = xmalloc(sizeof(char) * (strlen($2) + 2)); $$[0] = '-'; $$[1] = '\0'; strcat($$, $2); free($2); } | STRING | NUM_STRING | NUMBER ; arg_m4_quoted: QUOTED_STRING { $$ = sl_from_str_consume($1); } | mls_range { $$ = sl_from_str_consume($1); } | %empty { $$ = sl_from_str(""); } | BACKTICK arg_m4_quoted SINGLE_QUOTE { $$ = $2; } ; arg: arg_list | QUOTED_STRING { $$ = sl_from_str_consume($1); } | BACKTICK arg_m4_quoted SINGLE_QUOTE { $$ = $2; } ; args: arg { $1->arg_start = 1; $$ = $1;} | args COMMA arg { $3->arg_start = 1; $$ = concat_string_lists($1, $3); } | args sl_item { struct string_list *sl = xcalloc(1, sizeof(struct string_list)); sl->string = $2; sl->has_incorrect_space = 1; $1->arg_start = 1; $$ = concat_string_lists($1, sl); } ; mls_range: mls_level DASH mls_level { size_t len = strlen($1) + strlen($3) + 1 /* DASH */ + 1 /* NT */; $$ = xmalloc(len); snprintf($$, len, "%s-%s", $1, $3); free($1); free($3); } | mls_level ; mls_level: mls_component | mls_component COLON mls_component { size_t len = strlen($1) + strlen($3) + 1 /* COLON */ + 1 /* NT */; $$ = xmalloc(len); snprintf($$, len, "%s:%s", $1, $3); free($1); free($3); } ; mls_component: STRING | STRING PERIOD STRING { size_t len = strlen($1) + strlen($3) + 1 /* PERIOD */ + 1 /* NT */; $$ = xmalloc(len); snprintf($$, len, "%s.%s", $1, $3); free($1); free($3); } ; cond_expr: tunable_block | boolean_block ; boolean_block: boolean_open condition CLOSE_PAREN maybe_selint_disable OPEN_CURLY lines CLOSE_CURLY { end_boolean_policy(&cur); save_command(cur, $4); free($4); } | boolean_open condition CLOSE_PAREN maybe_selint_disable OPEN_CURLY lines CLOSE_CURLY ELSE OPEN_CURLY lines CLOSE_CURLY { end_boolean_policy(&cur); save_command(cur, $4); free($4); } ; boolean_open: IF OPEN_PAREN maybe_selint_disable { begin_boolean_policy(&cur, @$.first_line); save_command(cur->parent, $3); free($3); } ; tunable_block: TUNABLE_POLICY OPEN_PAREN BACKTICK { begin_tunable_policy(&cur, @$.first_line); } condition SINGLE_QUOTE maybe_selint_disable COMMA m4_args CLOSE_PAREN { end_tunable_policy(&cur); save_command(cur, $7); free($7); } | TUNABLE_POLICY OPEN_PAREN { begin_tunable_policy(&cur, @$.first_line); } condition maybe_selint_disable COMMA m4_args CLOSE_PAREN { end_tunable_policy(&cur); save_command(cur, $5); free($5); } ; genfscon: GENFSCON STRING string_or_quoted_string genfscon_context { free($2); free($3); } | GENFSCON NUM_STRING string_or_quoted_string genfscon_context { free($2); free($3); } ; genfscon_context: context | FILE_TYPE_SPECIFIER context ; sid: SID STRING context { free($2); } ; portcon: PORTCON STRING port_range context { free($2); } ; port_range: NUM_STRING { free($1); } | NUMBER { free($1); } | // TODO: This only happens with whitespace around the dash. NUM_STRING catches "1000-1001" type // names. Is that actually a valid scenario? NUMBER DASH NUMBER { free($1); free($3); } ; netifcon: NETIFCON STRING context context { free($2); } ; nodecon: NODECON two_ip_addrs context | NODECON cidr_addr context ; two_ip_addrs: IPV4 IPV4 { free($1); free($2); } | IPV6 IPV6 { free($1); free($2); } ; cidr_addr: IPV4_CIDR { free($1); } | IPV6_CIDR { free($1); } ; fs_use: FS_USE_TRANS STRING context SEMICOLON { free($2); } | FS_USE_XATTR STRING context SEMICOLON { free($2); } | FS_USE_TASK STRING context SEMICOLON { free($2); } ; define: DEFINE OPEN_PAREN { begin_define(&cur, @$.first_line); } define_name define_content CLOSE_PAREN { end_define(&cur); } ; define_name: BACKTICK STRING SINGLE_QUOTE { free($2); } | STRING { free($1); } ; define_content: %empty | COMMA define_expansion ; define_expansion: %empty | BACKTICK arbitrary_m4_string SINGLE_QUOTE | STRING { free($1); } | BACKTICK SINGLE_QUOTE ; maybe_string_comma: STRING COMMA | COMMA { $$ = xstrdup(""); } ; gen_user: GEN_USER OPEN_PAREN maybe_string_comma maybe_string_comma strings COMMA mls_range COMMA mls_range CLOSE_PAREN { free($3); free($4); free_string_list($5); free($7); free($9); } | GEN_USER OPEN_PAREN maybe_string_comma maybe_string_comma strings COMMA mls_range COMMA mls_range COMMA mls_range CLOSE_PAREN { free($3); free($4); free_string_list($5); free($7); free($9); free($11); } ; context: raw_context | GEN_CONTEXT OPEN_PAREN raw_context CLOSE_PAREN | GEN_CONTEXT OPEN_PAREN raw_context COMMA mls_range CLOSE_PAREN { free($5); } | GEN_CONTEXT OPEN_PAREN raw_context COMMA mls_range COMMA mls_range CLOSE_PAREN { free($5); free($7); } | GEN_CONTEXT OPEN_PAREN raw_context COMMA mls_range COMMA CLOSE_PAREN { free($5); } ; raw_context: STRING COLON STRING COLON STRING { free($1); free($3); free($5); } | STRING COLON STRING COLON STRING COLON mls_range { free($1); free($3); free($5); free($7); } ; permissive: PERMISSIVE STRING SEMICOLON { insert_permissive_statement(&cur, $2, @$.first_line); free($2);} ; typebounds: TYPEBOUNDS STRING STRING SEMICOLON { free($2); free($3); } ; // IF File parsing if_file: interface_def if_lines | interface_def | if_file_ifdef | if_file_ifdef if_lines //| // Empty file //EOF ; if_lines: if_lines if_line | if_line ; if_line: interface_def | COMMENT { insert_comment(&cur, @$.first_line); } | if_file_ifdef ; if_file_ifdef: ifdef_opener BACKTICK if_lines SINGLE_QUOTE CLOSE_PAREN { end_ifdef(&cur);} | ifdef_opener BACKTICK if_lines SINGLE_QUOTE COMMA BACKTICK if_lines SINGLE_QUOTE CLOSE_PAREN { end_ifdef(&cur);} ; interface_def: start_interface maybe_selint_disable lines end_interface { save_command(cur, $2); free($2); } | start_interface maybe_selint_disable end_interface { save_command(cur, $2); free($2); } ; start_interface: if_keyword OPEN_PAREN BACKTICK STRING SINGLE_QUOTE COMMA BACKTICK { if (expected_node_flavor != NODE_IF_FILE) { const struct location loc = { @1.first_line, @1.first_column, @7.last_line, @7.last_column }; yyerror(&loc, NULL, "Error: Unexpected if-file parsed"); YYERROR; } begin_interface_def(&cur, $1, $4, @$.first_line); free($4); } ; end_interface: SINGLE_QUOTE CLOSE_PAREN { end_interface_def(&cur); } ; if_keyword: INTERFACE { $$ = NODE_INTERFACE_DEF; } | TEMPLATE { $$ = NODE_TEMP_DEF; } ; // spt file parsing spt_file: support_def spt_lines | support_def ; spt_lines: spt_lines spt_line | spt_line ; spt_line: support_def | COMMENT ; support_def: DEFINE OPEN_PAREN BACKTICK STRING SINGLE_QUOTE COMMA BACKTICK spt_contents SINGLE_QUOTE CLOSE_PAREN { if (expected_node_flavor != NODE_SPT_FILE) { free($4); free_string_list($8); const struct location loc = { @1.first_line, @1.first_column, @10.last_line, @10.last_column }; yyerror(&loc, NULL, "Error: Unexpected spt-file parsed"); YYERROR; } if (ends_with($4, strlen($4), "_perms", strlen("_perms"))) { insert_into_permmacros_map($4, $8); } else { free_string_list($8); } free($4); } ; spt_contents: spt_contents spt_content { $$ = concat_string_lists($1, $2); } | spt_content ; spt_content: string_list | COMMENT { $$ = NULL; } | refpolicywarn { $$ = NULL; } ; // access-vector file av_file: // must not allow a comment to be first -> parser conflict av_definition av_contents | av_definition ; av_contents: av_contents av_content | av_content ; av_content: av_definition | COMMENT ; av_definition: av_class_definition | av_common_definition ; av_class_definition: CLASS STRING av_permission_list { if (expected_node_flavor != NODE_AV_FILE) { free($2); const struct location loc = { @1.first_line, @1.first_column, @3.last_line, @3.last_column }; yyerror(&loc, NULL, "Error: Unexpected av-file parsed"); YYERROR; } insert_into_decl_map($2, "__av_file__", DECL_CLASS); free($2); } | CLASS STRING INHERITS STRING { if (expected_node_flavor != NODE_AV_FILE) { free($2); free($4); const struct location loc = { @1.first_line, @1.first_column, @4.last_line, @4.last_column }; yyerror(&loc, NULL, "Error: Unexpected av-file parsed"); YYERROR; } insert_into_decl_map($2, "__av_file__", DECL_CLASS); free($2); free($4); } | CLASS STRING INHERITS STRING av_permission_list { if (expected_node_flavor != NODE_AV_FILE) { free($2); free($4); const struct location loc = { @1.first_line, @1.first_column, @5.last_line, @5.last_column }; yyerror(&loc, NULL, "Error: Unexpected av-file parsed"); YYERROR; } insert_into_decl_map($2, "__av_file__", DECL_CLASS); free($2); free($4); } ; av_common_definition: COMMON STRING av_permission_list { free($2); } ; av_permission_list: OPEN_CURLY av_permissions CLOSE_CURLY ; av_permissions: av_permissions av_permission | av_permission ; av_permission: STRING { insert_into_decl_map($1, "__av_file__", DECL_PERM); free($1); } | COMMENT ; // policy/global_booleans and policy/global_tunables files global_conditions_file: // must not allow a comment to be first -> parser conflict gcf_definition gcf_contents | gcf_definition ; gcf_contents: gcf_contents gcf_content | gcf_content ; gcf_content: gcf_definition | COMMENT ; gcf_definition: bool_declaration { if (expected_node_flavor != NODE_COND_FILE) { const struct location loc = { @1.first_line, @1.first_column, @1.last_line, @1.last_column }; yyerror(&loc, NULL, "Error: Unexpected global conditionals file parsed"); YYERROR; } } ; %% static unsigned leading_spaces(const char *str) { unsigned result = 0; while (str[result] == ' ' || str[result] == '\t') result++; return result; } static void yyerror(const YYLTYPE *locp, __attribute__((unused)) yyscan_t scanner, char const *msg) { // Print error tag: """test7.if: 1: (F): Error: Unexpected te-file parsed (F-001)""" { struct check_result *res = make_check_result('F', F_ID_POLICY_SYNTAX, "%s", msg); res->lineno = locp->first_line; IGNORE_CONST_DISCARD_BEGIN struct check_data data = { .mod_name = get_current_module_name(), .filepath = parsing_filename, .filename = parsing_filename, // Always print full path on errors .flavor = FILE_TE_FILE, // We don't know but it's unused by display_check_result }; IGNORE_CONST_DISCARD_END display_check_result(res, &data); free_check_result(res); } unsigned lines_to_print = locp->last_line - locp->first_line + 1; bool shortened = false; if (lines_to_print > LINES_TO_CACHE) { lines_to_print = LINES_TO_CACHE; shortened = true; printf("%5u | ... [truncated]\n", locp->last_line - LINES_TO_CACHE); } for (unsigned k = lines_to_print; k > 0; --k) { const char *current_line = trim_right(current_lines[(LINES_TO_CACHE + line_cache_index - k + 1) % LINES_TO_CACHE]); const unsigned current_first_column = (k == lines_to_print && !shortened) ? locp->first_column : (1 + leading_spaces(current_line)); const unsigned current_last_column = (k == 1) ? locp->last_column : (unsigned)strlen(current_line); printf("%5u |", locp->last_line - (k - 1)); // print line, replace tabs unsigned tabs_before_hinter = 0, tabs_inside_hinter = 0; if (*current_line != '\0') { printf(" "); } for (const char *c = current_line; *c != '\0'; ++c) { if (*c == '\t') { if ((size_t)(c - current_line) < current_first_column) { tabs_before_hinter++; } else if ((size_t)(c - current_line) < current_last_column) { tabs_inside_hinter++; } printf(" "); continue; } if (!isprint((unsigned char)*c) && !isspace((unsigned char)*c)) { printf("%s!%s\n%sWarning%s: Line in question contains unprintable character at position %zu: 0x%.2x\n", color_error(), color_reset(), color_warning(), color_reset(), (size_t)(c - current_line + 1), *c); return; } printf("%c", *c); } printf("\n | "); // print hinter for (unsigned i = 0; i < tabs_before_hinter; ++i) { printf(" "); } for (unsigned i = tabs_before_hinter + 1; i < current_first_column; ++i) { printf(" "); } if (k == lines_to_print) { printf("%s^", color_error()); } else { printf("%s~", color_error()); } if (current_last_column > current_first_column) { for (unsigned i = 0; i < (current_last_column - current_first_column); ++i) { printf("~"); } for (unsigned i = 0; i < tabs_inside_hinter; ++i) { printf("~~~"); } } printf("%s\n", color_reset()); } } struct policy_node *yyparse_wrapper(FILE *filefd, const char *filename, enum node_flavor expected_flavor) { struct policy_node *ast = xcalloc(1, sizeof(struct policy_node)); ast->flavor = expected_node_flavor = expected_flavor; yyscan_t scanner; yylex_init(&scanner); yyrestart(filefd, scanner); parsing_filename = filename; cur = ast; const int ret = yyparse(scanner); reset_current_lines(); yylex_destroy(scanner); if (ret != 0) { // parser will have printed an error message free_policy_node(ast); return NULL; } return ast; } selint-1.5.1/src/parse_fc.c000066400000000000000000000144621475050262500155460ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "parse_fc.h" #include "tree.h" #include "xalloc.h" // "gen_context(" #define GEN_CONTEXT_LEN 12 struct fc_entry *parse_fc_line(char *line) { const char *whitespace = " \t"; struct fc_entry *out = xmalloc(sizeof(struct fc_entry)); memset(out, 0, sizeof(struct fc_entry)); char *orig_line = xstrdup(line); // If the object class is omitted, we need to revert char *pos = strtok(line, whitespace); if (pos == NULL) { goto cleanup; } out->path = xstrdup(pos); pos = strtok(NULL, whitespace); if (pos == NULL) { goto cleanup; } if (pos[0] == '-') { if (pos[2] != '\0') { goto cleanup; } out->obj = pos[1]; pos = strtok(NULL, whitespace); if (pos == NULL) { goto cleanup; } } // pos points to the start of the context, but spaces in the context may have been // overwritten by strtok strcpy(line, orig_line); if (strncmp("gen_context(", pos, GEN_CONTEXT_LEN) == 0) { pos += GEN_CONTEXT_LEN; // Next character char *context_part = strtok(pos, ","); if (context_part == NULL) { goto cleanup; } char *maybe_s = strtok(NULL, ","); char *maybe_c = NULL; int i = 0; if (maybe_s) { maybe_c = strtok(NULL, ","); while (maybe_s[i] != '\0' && maybe_s[i] != ')') { i++; } if (maybe_s[i] == '\0') { if (!maybe_c) { // Missing closing paren goto cleanup; } } maybe_s[i] = '\0'; while (maybe_s[0] != '\0' && (maybe_s[0] == ' ' || maybe_s[0] == '\t')) { // trim beginning whitespace maybe_s++; } } else { // No mls while (context_part[i] != '\0' && context_part[i] != ')') { i++; } if (context_part[i] == '\0') { // Missing closing paren goto cleanup; } context_part[i] = '\0'; } if (maybe_c) { while (maybe_c[i] != '\0' && maybe_c[i] != ')') { i++; } if (maybe_c[i] == '\0') { // Missing closing paren goto cleanup; } maybe_c[i] = '\0'; while (maybe_c[0] != '\0' && (maybe_c[0] == ' ' || maybe_c[0] == '\t')) { // trim beginning whitespace maybe_c++; } } out->context = parse_context(context_part); if (out->context == NULL) { goto cleanup; } out->context->has_gen_context = 1; if (maybe_c) { out->context->range = xmalloc(strlen(maybe_s) + 1 + strlen(maybe_c) + 1); strcpy(out->context->range, maybe_s); strcat(out->context->range, ":"); strcat(out->context->range, maybe_c); } else if (maybe_s) { out->context->range = xstrdup(maybe_s); } else { out->context->range = NULL; } } else if (strcmp("<>", pos) == 0) { out->context = NULL; } else { out->context = parse_context(pos); if (out->context == NULL) { goto cleanup; } out->context->has_gen_context = 0; } free(orig_line); return out; cleanup: free(orig_line); free_fc_entry(out); return NULL; } struct sel_context *parse_context(char *context_str) { if (strchr(context_str, '(')) { return NULL; } struct sel_context *context = xmalloc(sizeof(struct sel_context)); memset(context, 0, sizeof(struct sel_context)); // User const char *pos = strtok(context_str, ":"); if (pos == NULL) { goto cleanup; } context->user = xstrdup(pos); // Role pos = strtok(NULL, ":"); if (pos == NULL) { goto cleanup; } context->role = xstrdup(pos); // Type pos = strtok(NULL, ":"); if (pos == NULL) { goto cleanup; } context->type = xstrdup(pos); pos = strtok(NULL, ":"); if (pos) { context->range = xstrdup(pos); if (strtok(NULL, ":")) { goto cleanup; } } return context; cleanup: free_sel_context(context); return NULL; } bool check_for_fc_macro(const char *line, const struct string_list *custom_fc_macros) { if (!custom_fc_macros) { return false; } size_t line_len = strlen(line); for (;custom_fc_macros; custom_fc_macros = custom_fc_macros->next){ size_t custom_fc_len = strlen(custom_fc_macros->string); if (line_len <= custom_fc_len) { continue; } if (line[custom_fc_len] != '(') { continue; } if (0 == strncmp(line, custom_fc_macros->string, custom_fc_len)) { return true; } } return false; } static void rtrim(char *line, size_t len) { while (len > 0 && isspace((unsigned char)line[len - 1])) line[--len] = '\0'; } struct policy_node *parse_fc_file(const char *filename, const struct string_list *custom_fc_macros) { FILE *fd = fopen(filename, "re"); if (!fd) { return NULL; } struct policy_node *head = xmalloc(sizeof(struct policy_node)); memset(head, 0, sizeof(struct policy_node)); head->flavor = NODE_FC_FILE; struct policy_node *cur = head; char *line = NULL; ssize_t len_read = 0; size_t buf_len = 0; unsigned int lineno = 0; while ((len_read = getline(&line, &buf_len, fd)) != -1) { lineno++; if (len_read <= 1 || line[0] == '#') { continue; } // Drop trailing white spaces rtrim(line, (size_t)len_read); // Skip over m4 constructs if (strncmp(line, "ifdef", 5) == 0 || strncmp(line, "ifndef", 6) == 0 || strncmp(line, "')", 2) == 0 || strncmp(line, "', `", 4) == 0 || strncmp(line, "',`", 3) == 0) { continue; } if (check_for_fc_macro(line, custom_fc_macros)) { continue; } struct fc_entry *entry = parse_fc_line(line); enum node_flavor flavor; if (entry == NULL) { if (*line == '\0') { flavor = NODE_EMPTY; } else { flavor = NODE_ERROR; } } else { flavor = NODE_FC_ENTRY; } union node_data nd; nd.fc_data = entry; if (insert_policy_node_next(cur, flavor, nd, lineno) != SELINT_SUCCESS) { free_policy_node(head); fclose(fd); return NULL; } cur = cur->next; free(line); line = NULL; buf_len = 0; } free(line); // getline alloc must be freed even if getline failed fclose(fd); return head; } selint-1.5.1/src/parse_fc.h000066400000000000000000000023171475050262500155470ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PARSE_FC_H #define PARSE_FC_H #include #include "tree.h" // Takes in a null terminated string that is an fc entry and populates an fc_entry struct struct fc_entry *parse_fc_line(char *line); struct sel_context *parse_context(char *context_str); // Return true if the line contains a defined custom fc macro, and false otherwise bool check_for_fc_macro(const char *line, const struct string_list *custom_fc_macros); // Parse an fc file and return a pointer to an abstract syntax tree representing the file struct policy_node *parse_fc_file(const char *filename, const struct string_list *custom_fc_macros); #endif selint-1.5.1/src/parse_functions.c000066400000000000000000000540371475050262500171700ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "parse_functions.h" #include "selint_error.h" #include "tree.h" #include "template.h" #include "util.h" #include "perm_macro.h" #include "xalloc.h" static char *module_name = NULL; enum selint_error insert_header(struct policy_node **cur, const char *mn, enum header_flavor flavor, unsigned int lineno) { struct header_data *data = (struct header_data *)xmalloc(sizeof(struct header_data)); if (!data) { return SELINT_OUT_OF_MEM; } memset(data, 0, sizeof(struct header_data)); data->flavor = flavor; data->module_name = xstrdup(mn); if (!data->module_name) { free(data); return SELINT_OUT_OF_MEM; } union node_data nd; nd.h_data = data; enum selint_error ret = insert_policy_node_next(*cur, NODE_HEADER, nd, lineno); if (ret != SELINT_SUCCESS) { return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } void set_current_module_name(const char *mn) { if (module_name != NULL) { free(module_name); } module_name = xstrdup(mn); } char *get_current_module_name(void) { return module_name; } enum selint_error insert_comment(struct policy_node **cur, unsigned int lineno) { union node_data data; data.str = NULL; enum selint_error ret = insert_policy_node_next(*cur, NODE_COMMENT, data, lineno); if (ret != SELINT_SUCCESS) { return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_declaration(struct policy_node **cur, enum decl_flavor flavor, const char *name, struct string_list *attrs, unsigned int lineno) { if (!is_in_require(*cur)) { // In a require block, the objects aren't being declared // Otherwise, we need to insert them into the appropriate map const char *temp_name = get_name_if_in_template(*cur); if (temp_name) { // We are inside a template, so we need to save declarations in the template map // 'role foo types bar_t, baz_t;' statements are not declarations. insert_decl_into_template_map(temp_name, flavor, name); } else if ('$' != name[0]) { // If the name starts with $ we're probably doing something like associating // a role with types in interfaces const char *mn = get_current_module_name(); if (!mn) { return SELINT_NO_MOD_NAME; } insert_into_decl_map(name, mn, flavor); } } struct declaration_data *data = (struct declaration_data *)xmalloc(sizeof(struct declaration_data)); if (!data) { return SELINT_OUT_OF_MEM; } memset(data, 0, sizeof(struct declaration_data)); data->flavor = flavor; data->name = xstrdup(name); data->attrs = attrs; union node_data nd; nd.d_data = data; enum selint_error ret = insert_policy_node_next(*cur, NODE_DECL, nd, lineno); if (ret != SELINT_SUCCESS) { free(data); return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_aliases(struct policy_node **cur, struct string_list *aliases, enum decl_flavor flavor, unsigned int lineno) { struct string_list *alias = aliases; while (alias) { const char *temp_name = get_name_if_in_template(*cur); if (temp_name) { insert_decl_into_template_map(temp_name, flavor, alias->string); } else { const char *mn = get_current_module_name(); if (!mn) { free_string_list(aliases); return SELINT_NO_MOD_NAME; } insert_into_decl_map(alias->string, mn, flavor); } union node_data nd; nd.str = xstrdup(alias->string); enum selint_error ret = insert_policy_node_child(*cur, NODE_ALIAS, nd, lineno); if (ret != SELINT_SUCCESS) { return ret; } alias = alias->next; } free_string_list(aliases); return SELINT_SUCCESS; } enum selint_error insert_type_alias(struct policy_node **cur, const char *type, unsigned int lineno) { union node_data nd; nd.str = xstrdup(type); enum selint_error ret = insert_policy_node_next(*cur, NODE_TYPE_ALIAS, nd, lineno); if (ret != SELINT_SUCCESS) { return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_av_rule(struct policy_node **cur, enum av_rule_flavor flavor, struct string_list *sources, struct string_list *targets, struct string_list *object_classes, struct string_list *perms, unsigned int lineno) { struct av_rule_data *av_data = xmalloc(sizeof(struct av_rule_data)); av_data->flavor = flavor; av_data->sources = sources; av_data->targets = targets; av_data->object_classes = object_classes; av_data->perms = perms; union node_data nd; nd.av_data = av_data; if ((*cur)->parent && (*cur)->parent->flavor == NODE_INTERFACE_DEF && str_in_sl("associate", perms)) { mark_transform_if((*cur)->parent->data.str); } enum selint_error ret = insert_policy_node_next(*cur, NODE_AV_RULE, nd, lineno); if (ret != SELINT_SUCCESS) { free_av_rule_data(av_data); return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_xperm_av_rule(struct policy_node **cur, enum av_rule_flavor flavor, struct string_list *sources, struct string_list *targets, struct string_list *object_classes, const char *operation, struct string_list *perms, unsigned int lineno) { struct xav_rule_data *xav_data = xmalloc(sizeof(struct xav_rule_data)); xav_data->flavor = flavor; xav_data->sources = sources; xav_data->targets = targets; xav_data->object_classes = object_classes; xav_data->operation = xstrdup(operation); xav_data->perms = perms; union node_data nd; nd.xav_data = xav_data; enum selint_error ret = insert_policy_node_next(*cur, NODE_XAV_RULE, nd, lineno); if (ret != SELINT_SUCCESS) { free_xav_rule_data(xav_data); return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_role_allow(struct policy_node **cur, struct string_list *from_roles, struct string_list *to_roles, unsigned int lineno) { struct role_allow_data *ra_data = xmalloc(sizeof(struct role_allow_data)); ra_data->from = from_roles; ra_data->to = to_roles; union node_data nd; nd.ra_data = ra_data; enum selint_error ret = insert_policy_node_next(*cur, NODE_ROLE_ALLOW, nd, lineno); if (ret != SELINT_SUCCESS) { free_ra_data(ra_data); return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_role_types(struct policy_node **cur, const char *role, struct string_list *types, unsigned int lineno) { if ((*cur)->parent && (*cur)->parent->flavor == NODE_INTERFACE_DEF) { const struct string_list *cur_sl_item = types; while (cur_sl_item) { if (cur_sl_item->string[0] == '$') { // Role interfaces are only those where the types are passed in, not the roles mark_role_if((*cur)->parent->data.str); break; } cur_sl_item = cur_sl_item->next; } } struct role_types_data *rtyp_data = (struct role_types_data *)xmalloc(sizeof(struct role_types_data)); rtyp_data->role = xstrdup(role); rtyp_data->types = types; union node_data nd; nd.rtyp_data = rtyp_data; enum selint_error ret = insert_policy_node_next(*cur, NODE_ROLE_TYPES, nd, lineno); if (ret != SELINT_SUCCESS) { free_rtyp_data(rtyp_data); return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_type_transition(struct policy_node **cur, enum tt_flavor flavor, struct string_list *sources, struct string_list *targets, struct string_list *object_classes, const char *default_type, const char *name, unsigned int lineno) { struct type_transition_data *tt_data = xmalloc(sizeof(struct type_transition_data)); tt_data->sources = sources; tt_data->targets = targets; tt_data->object_classes = object_classes; tt_data->default_type = xstrdup(default_type); if (name) { tt_data->name = xstrdup(name); } else { tt_data->name = NULL; } tt_data->flavor = flavor; if (!str_in_sl("process", object_classes) && (*cur)->parent && (*cur)->parent->flavor == NODE_INTERFACE_DEF) { mark_filetrans_if((*cur)->parent->data.str); } union node_data nd; nd.tt_data = tt_data; enum selint_error ret = insert_policy_node_next(*cur, NODE_TT_RULE, nd, lineno); if (ret != SELINT_SUCCESS) { free_type_transition_data(tt_data); return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_role_transition(struct policy_node **cur, struct string_list *sources, struct string_list *targets, struct string_list *object_classes, const char *default_role, unsigned int lineno) { struct role_transition_data *rt_data = xmalloc(sizeof(struct role_transition_data)); rt_data->sources = sources; rt_data->targets = targets; rt_data->object_classes = object_classes; rt_data->default_role = xstrdup(default_role); union node_data nd; nd.rt_data = rt_data; enum selint_error ret = insert_policy_node_next(*cur, NODE_RT_RULE, nd, lineno); if (ret != SELINT_SUCCESS) { free_role_transition_data(rt_data); return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } static int is_filetrans_if_name(const char *if_name) { if (0 == strcmp(if_name, "filetrans_pattern")) { return 1; } if (0 == strcmp(if_name, "filetrans_add_pattern")) { return 1; } const char *suffix = strrchr(if_name, '_'); if (suffix && (0 == strcmp(suffix, "_filetrans"))) { return 1; } return 0; } enum selint_error insert_interface_call(struct policy_node **cur, const char *if_name, struct string_list *args, unsigned int lineno) { struct if_call_data *if_data = xmalloc(sizeof(struct if_call_data)); if_data->name = xstrdup(if_name); if_data->args = args; const char *template_name = get_name_if_in_template(*cur); if (template_name) { insert_call_into_template_map(template_name, if_data); } else if (!is_in_if_define(*cur)) { enum selint_error r = add_template_declarations(if_name, args, NULL, module_name); if (r != SELINT_SUCCESS) { free_if_call_data(if_data); return r; } } if (is_filetrans_if_name(if_name) && (*cur)->parent && (*cur)->parent->flavor == NODE_INTERFACE_DEF) { mark_filetrans_if((*cur)->parent->data.str); } mark_used_if(if_name); union node_data nd; nd.ic_data = if_data; enum selint_error ret = insert_policy_node_next(*cur, NODE_IF_CALL, nd, lineno); if (ret != SELINT_SUCCESS) { free_if_call_data(if_data); return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_permissive_statement(struct policy_node **cur, const char *domain, unsigned int lineno) { union node_data nd; nd.str = xstrdup(domain); enum selint_error ret = insert_policy_node_next(*cur, NODE_PERMISSIVE, nd, lineno); if (ret != SELINT_SUCCESS) { return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_semicolon(struct policy_node **cur, unsigned int lineno) { union node_data nd; nd.str = NULL; enum selint_error ret = insert_policy_node_next(*cur, NODE_SEMICOLON, nd, lineno); if (ret != SELINT_SUCCESS) { return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } enum selint_error insert_m4simplemacro(struct policy_node **cur, char *name, unsigned int lineno) { union node_data nd; nd.str = name; enum selint_error ret = insert_policy_node_next(*cur, NODE_M4_SIMPLE_MACRO, nd, lineno); if (ret != SELINT_SUCCESS) { return ret; } *cur = (*cur)->next; return SELINT_SUCCESS; } static enum selint_error begin_block(struct policy_node **cur, enum node_flavor block_type, union node_data nd, unsigned int lineno) { enum selint_error ret = insert_policy_node_next(*cur, block_type, nd, lineno); if (ret != SELINT_SUCCESS) { return ret; } *cur = (*cur)->next; nd.str = NULL; ret = insert_policy_node_child(*cur, NODE_START_BLOCK, nd, lineno); if (ret != SELINT_SUCCESS) { *cur = (*cur)->prev; free_policy_node((*cur)->next); return ret; } *cur = (*cur)->first_child; return SELINT_SUCCESS; } static enum selint_error end_block(struct policy_node **cur, enum node_flavor block_type) { if ((*cur)->parent == NULL || (*cur)->parent->flavor != block_type) { return SELINT_NOT_IN_BLOCK; } *cur = (*cur)->parent; return SELINT_SUCCESS; } enum selint_error begin_define(struct policy_node **cur, unsigned int lineno) { union node_data nd; nd.str = NULL; return begin_block(cur, NODE_DEFINE, nd, lineno); } enum selint_error end_define(struct policy_node **cur) { return end_block(cur, NODE_DEFINE); } enum selint_error begin_optional_policy(struct policy_node **cur, unsigned int lineno) { union node_data nd; nd.str = NULL; return begin_block(cur, NODE_OPTIONAL_POLICY, nd, lineno); } enum selint_error end_optional_policy(struct policy_node **cur) { return end_block(cur, NODE_OPTIONAL_POLICY); } enum selint_error begin_optional_else(struct policy_node **cur, unsigned int lineno) { union node_data nd; nd.str = NULL; return begin_block(cur, NODE_OPTIONAL_ELSE, nd, lineno); } enum selint_error end_optional_else(struct policy_node **cur) { return end_block(cur, NODE_OPTIONAL_ELSE); } enum selint_error begin_boolean_policy(struct policy_node **cur, unsigned int lineno) { struct cond_declaration_data *cd_data = xmalloc(sizeof(struct cond_declaration_data)); cd_data->identifiers = NULL; union node_data nd; nd.cd_data = cd_data; return begin_block(cur, NODE_BOOLEAN_POLICY, nd, lineno); } enum selint_error end_boolean_policy(struct policy_node **cur) { return end_block(cur, NODE_BOOLEAN_POLICY); } enum selint_error begin_tunable_policy(struct policy_node **cur, unsigned int lineno) { struct cond_declaration_data *cd_data = xmalloc(sizeof(struct cond_declaration_data)); cd_data->identifiers = NULL; union node_data nd; nd.cd_data = cd_data; return begin_block(cur, NODE_TUNABLE_POLICY, nd, lineno); } enum selint_error end_tunable_policy(struct policy_node **cur) { return end_block(cur, NODE_TUNABLE_POLICY); } enum selint_error begin_interface_def(struct policy_node **cur, enum node_flavor flavor, const char *name, unsigned int lineno) { switch (flavor) { case NODE_INTERFACE_DEF: break; case NODE_TEMP_DEF: insert_template_into_template_map(name); break; default: return SELINT_BAD_ARG; } insert_into_ifs_map(name, get_current_module_name()); union node_data nd; nd.str = xstrdup(name); return begin_block(cur, flavor, nd, lineno); } enum selint_error end_interface_def(struct policy_node **cur) { if (end_block(cur, NODE_INTERFACE_DEF) == SELINT_NOT_IN_BLOCK) { return end_block(cur, NODE_TEMP_DEF); } else { return SELINT_SUCCESS; } } enum selint_error begin_gen_require(struct policy_node **cur, unsigned int lineno) { struct gen_require_data *data = (struct gen_require_data *)xmalloc(sizeof(struct gen_require_data)); union node_data nd; nd.gr_data = data; return begin_block(cur, NODE_GEN_REQ, nd, lineno); } enum selint_error end_gen_require(struct policy_node **cur, unsigned char unquoted) { if ((*cur)->parent && (*cur)->parent->flavor == NODE_GEN_REQ) { (*cur)->parent->data.gr_data->unquoted = unquoted; } return end_block(cur, NODE_GEN_REQ); } enum selint_error begin_require(struct policy_node **cur, unsigned int lineno) { union node_data nd; nd.str = NULL; return begin_block(cur, NODE_REQUIRE, nd, lineno); } enum selint_error end_require(struct policy_node **cur) { return end_block(cur, NODE_REQUIRE); } enum selint_error begin_ifdef(struct policy_node **cur, unsigned int lineno) { union node_data nd; nd.str = NULL; return begin_block(cur, NODE_IFDEF, nd, lineno); } enum selint_error end_ifdef(struct policy_node **cur) { return end_block(cur, NODE_IFDEF); } enum selint_error begin_ifelse(struct policy_node **cur, unsigned int lineno) { union node_data nd; nd.str = NULL; return begin_block(cur, NODE_IFELSE, nd, lineno); } enum selint_error end_ifelse(struct policy_node **cur) { return end_block(cur, NODE_IFELSE); } enum selint_error begin_m4_argument(struct policy_node **cur, unsigned int lineno) { union node_data nd; nd.str = NULL; return begin_block(cur, NODE_M4_ARG, nd, lineno); } enum selint_error end_m4_argument(struct policy_node **cur) { return end_block(cur, NODE_M4_ARG); } enum selint_error save_command(struct policy_node *cur, const char *comm) { if (cur == NULL) { return SELINT_BAD_ARG; } if (comm == NULL) { return SELINT_SUCCESS; } while (*comm != 's' && *comm != '\0') { comm++; } if (0 != strncmp("selint-", comm, 7)) { return SELINT_PARSE_ERROR; } comm += strlen("selint-"); if (0 == strncmp("disable:", comm, 8)) { cur->exceptions = xstrdup(comm + strlen("disable:")); } else { return SELINT_PARSE_ERROR; } return SELINT_SUCCESS; } enum selint_error save_identifier(struct policy_node *cur, char *identifier) { if (cur == NULL || identifier == NULL) { free(identifier); return SELINT_BAD_ARG; } if (cur->flavor != NODE_TUNABLE_POLICY && cur->flavor != NODE_BOOLEAN_POLICY) { free(identifier); return SELINT_BAD_ARG; } cur->data.cd_data->identifiers = concat_string_lists(cur->data.cd_data->identifiers, sl_from_str_consume(identifier)); return SELINT_SUCCESS; } static enum node_flavor attr_to_node_flavor(enum attr_flavor flavor) { switch (flavor) { case ATTR_TYPE: return NODE_TYPE_ATTRIBUTE; case ATTR_ROLE: return NODE_ROLE_ATTRIBUTE; default: // Should never happen return NODE_ERROR; } } static enum selint_error insert_attribute(struct policy_node **cur, enum attr_flavor flavor, const char *type, struct string_list *attrs, unsigned int lineno) { struct attribute_data *data = xcalloc(1, sizeof(struct attribute_data)); union node_data nd; nd.at_data = data; data->type = xstrdup(type); data->attrs = attrs; data->flavor = flavor; enum selint_error ret = insert_policy_node_next(*cur, attr_to_node_flavor(flavor), nd, lineno); if (ret != SELINT_SUCCESS) { free(data); return ret; } *cur = (*cur)->next; if ((*cur)->parent && (*cur)->parent->flavor == NODE_INTERFACE_DEF && (is_transform_interface((*cur)->parent->data.str) || 0 == strcmp(get_current_module_name(), "mls") || 0 == strcmp(get_current_module_name(), "mcs"))) { mark_transform_if((*cur)->parent->data.str); } return SELINT_SUCCESS; } enum selint_error insert_type_attribute(struct policy_node **cur, const char *type, struct string_list *attrs, unsigned int lineno) { return insert_attribute(cur, ATTR_TYPE, type, attrs, lineno); } enum selint_error insert_role_attribute(struct policy_node **cur, const char *role, struct string_list *attrs, unsigned int lineno) { return insert_attribute(cur, ATTR_ROLE, role, attrs, lineno); } void cleanup_parsing(void) { if (module_name) { free(module_name); module_name = NULL; } free_permmacros(); free_all_maps(); } selint-1.5.1/src/parse_functions.h000066400000000000000000000417451475050262500171770ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PARSING_FUNCTIONS_H #define PARSING_FUNCTIONS_H #include "selint_error.h" #include "tree.h" #include "maps.h" /********************************** * insert_header * Add a header node at the next node in the tree, allocating all memory for it. * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated declaration node * module_name (in) - The name of the policy module * flavor (in) - The flavor being declared * lineno (in) - The line number * Returns - SELINT error code **********************************/ enum selint_error insert_header(struct policy_node **cur, const char *mn, enum header_flavor flavor, unsigned int lineno); /********************************** * Set the name of the current module to mn **********************************/ void set_current_module_name(const char *mn); /********************************** * Return the name of the current module * This is only available during parsing **********************************/ char *get_current_module_name(void); /********************************** * insert_comment * Add a comment node at the next node in the tree, allocating all memory for it. * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated declaration node * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error insert_comment(struct policy_node **cur, unsigned int lineno); /********************************** * insert_declaration * Add a declaration node at the next node in the tree, allocating all memory for it * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated declaration node * flavor (in)- What sort of declaration this is * name (in) - The name of the item being declared * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error insert_declaration(struct policy_node **cur, enum decl_flavor flavor, const char *name, struct string_list *attrs, unsigned int lineno); /********************************** * insert_aliases * Add alias nodes below the declaration and insert the aliases into the type map * cur (in) - The current spot in the tree. Will not be changed * aliases (in) - The aliases. This function will free the list * flavor (in) - The flavor being declared * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error insert_aliases(struct policy_node **cur, struct string_list *aliases, enum decl_flavor flavor, unsigned int lineno); /********************************** * insert_type_alias * Add a typealias rule node at the next node in the tree, allocating all memory for it * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated av rule node * type (in) - The name of the type in the node. * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error insert_type_alias(struct policy_node **cur, const char *type, unsigned int lineno); /********************************** * insert_av_rule * Add an av rule node at the next node in the tree, allocating all memory for it * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated av rule node * flavor (in) - What sort of av rule this is * sources (in) - (memory allocated by caller) the sources in the rule * targets (in) - (memory allocated by caller) the targets in the rule * object_classes (in) - (memory allocated by caller) the object classes in the rule * perms (in) - (memory allocated by caller) the perms in the rule * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error insert_av_rule(struct policy_node **cur, enum av_rule_flavor flavor, struct string_list *sources, struct string_list *targets, struct string_list *object_classes, struct string_list *perms, unsigned int lineno); /********************************** * insert_xperm_av_rule * Add an extended permission av rule node at the next node in the tree, allocating all memory for it * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated av rule node * flavor (in) - What sort of av rule this is * sources (in) - (memory allocated by caller) the sources in the rule * targets (in) - (memory allocated by caller) the targets in the rule * object_classes (in) - (memory allocated by caller) the object classes in the rule * operation (in) - the operation implemented by this rule * perms (in) - (memory allocated by caller) the extended permission in the rule * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error insert_xperm_av_rule(struct policy_node **cur, enum av_rule_flavor flavor, struct string_list *sources, struct string_list *targets, struct string_list *object_classes, const char *operation, struct string_list *perms, unsigned int lineno); /********************************** * insert_role_allow * Add a role allow node at the next node in the tree, allocating all memory for it * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated av rule node * from_role (in) - The role allowed to transition from * to_role (in) - The role allowed to transition to * lineno (in) - The line number of the rule * * Returns - SELINT error code **********************************/ enum selint_error insert_role_allow(struct policy_node **cur, struct string_list *from_roles, struct string_list *to_roles, unsigned int lineno); /********************************** * insert_role_types * Add a role types node at the next node in the tree, allocating all memory for it * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated av rule node * role (in) - The name of the role allowed to access types * types (in) - (memory allocated by caller) The types allowed to be accessed * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error insert_role_types(struct policy_node **cur, const char *role, struct string_list *types, unsigned int lineno); /********************************** * insert_type_transition * Add a type transition node at the next node in the tree, allocating all memory for it * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated type transition node * flavor (in) - The variety of type transition role. The normal case is type_transition (TT_TT), * but other options include type_member (TT_TM), type_change (TT_TC) and range_transition (TT_RT) * sources (in) - (memory allocated by caller) The sources in the rule * targets (in) - (memory allocated by caller) the targets in the rule * object_classes (in) - (memory allocated by caller) the object classes in the rule * default_type (in) - The type to transition to * name (in) - The name of the file for named transitions. Can be NULL if not specified. * lineno (in) - The line number of the rule * * Returns - SELINT error code **********************************/ enum selint_error insert_type_transition(struct policy_node **cur, enum tt_flavor flavor, struct string_list *sources, struct string_list *targets, struct string_list *object_classes, const char *default_type, const char *name, unsigned int lineno); /********************************** * insert_role_transition * Add a role transition node at the next node in the tree, allocating all memory for it * cur (in, out) - The current spot in the tree. Will be updated to point to * the newly allocated role transition node * sources (in) - (memory allocated by caller) The sources in the rule * targets (in) - (memory allocated by caller) the targets in the rule * object_classes (in) - (memory allocated by caller) the object classes in the rule * default_role (in) - The role to transition to * lineno (in) - The line number of the rule * * Returns - SELINT error code **********************************/ enum selint_error insert_role_transition(struct policy_node **cur, struct string_list *sources, struct string_list *targets, struct string_list *object_classes, const char *default_role, unsigned int lineno); enum selint_error insert_interface_call(struct policy_node **cur, const char *if_name, struct string_list *args, unsigned int lineno); enum selint_error insert_permissive_statement(struct policy_node **cur, const char *domain, unsigned int lineno); enum selint_error insert_semicolon(struct policy_node **cur, unsigned int lineno); enum selint_error insert_m4simplemacro(struct policy_node **cur, char *name, unsigned int lineno); /********************************** * begin_define * Add a define() node at the next node in the tree. Create its first child * as the start of the block. Set cur to the child node. Allocate all memory for * both nodes. * cur (in, out) - The current spot in the tree. Will be updated to point to the * first child of the define() node * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error begin_define(struct policy_node **cur, unsigned int lineno); /********************************** * end_define * Complete the define() block by moving cur back up to the parent level * cur (in, out) - The current spot in the tree. Will be updated to point to the * parent define() node * * Returns - SELINT error code **********************************/ enum selint_error end_define(struct policy_node **cur); /********************************** * begin_optional_policy * Add an optional policy node at the next node in the tree. Create its first child * as the start of the block. Set cur to the child node. Allocate all memory for * both nodes. * cur (in, out) - The current spot in the tree. Will be updated to point to the * first child of the optional_policy node * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error begin_optional_policy(struct policy_node **cur, unsigned int lineno); /********************************** * end_optional_policy * Complete the optional policy block by moving cur back up to the parent level * cur (in, out) - The current spot in the tree. Will be updated to point to the * parent optional policy node * * Returns - SELINT error code **********************************/ enum selint_error end_optional_policy(struct policy_node **cur); /********************************** * begin_optional_else * Add the else portion of an optional policy node at the next node in the tree. Create its first child * as the start of the block. Set cur to the child node. Allocate all memory for * both nodes. * cur (in, out) - The current spot in the tree. Will be updated to point to the * first child of the optional_policy node * lineno (in) - The line number * * Returns - SELINT error code **********************************/ enum selint_error begin_optional_else(struct policy_node **cur, unsigned int lineno); /********************************** * end_optional_policy * Complete the optional policy else block by moving cur back up to the parent level * cur (in, out) - The current spot in the tree. Will be updated to point to the * parent optional policy node * * Returns - SELINT error code **********************************/ enum selint_error end_optional_else(struct policy_node **cur); enum selint_error begin_tunable_policy(struct policy_node **cur, unsigned int lineno); enum selint_error end_tunable_policy(struct policy_node **cur); enum selint_error begin_boolean_policy(struct policy_node **cur, unsigned int lineno); enum selint_error end_boolean_policy(struct policy_node **cur); enum selint_error begin_interface_def(struct policy_node **cur, enum node_flavor flavor, const char *name, unsigned int lineno); enum selint_error end_interface_def(struct policy_node **cur); enum selint_error begin_gen_require(struct policy_node **cur, unsigned int lineno); enum selint_error end_gen_require(struct policy_node **cur, unsigned char unquoted); enum selint_error begin_require(struct policy_node **cur, unsigned int lineno); enum selint_error end_require(struct policy_node **cur); enum selint_error begin_ifdef(struct policy_node **cur, unsigned int lineno); enum selint_error end_ifdef(struct policy_node **cur); enum selint_error begin_ifelse(struct policy_node **cur, unsigned int lineno); enum selint_error end_ifelse(struct policy_node **cur); enum selint_error begin_m4_argument(struct policy_node **cur, unsigned int lineno); enum selint_error end_m4_argument(struct policy_node **cur); /********************************** * save_command * Save an selint control command in the tree. These go at the end of lines * and modify selint behavior while checking that line. * Current commands are: * - selint-disable:[check-id] * cur (in) - The current spot in the tree. Will be modified with information * about the command * comm (in) - What command string was in the comment * * Returns - SELint error code **********************************/ enum selint_error save_command(struct policy_node *cur, const char *comm); /********************************** * save_identifier * Save an identifier name in the tree. * cur (in) - The current spot in the tree. Will be modified with information * about the identifier * comm (in, sink) - The name of the identifier * * Returns - SELint error code **********************************/ enum selint_error save_identifier(struct policy_node *cur, char *identifier); /********************************** * insert_type_attribute * Insert a type_attribute node into the tree * cur (in, out) - The current spot in the tree. Will be updated to point to the * new node. * type (in) - The type specified in the statement * attrs (in) - The attributes specified in the statement * lineno (in) - The line number * * Returns - SELint error code **********************************/ enum selint_error insert_type_attribute(struct policy_node **cur, const char *type, struct string_list *attrs, unsigned int lineno); /********************************** * insert_role_attribute * Insert a role_attribute node into the tree * cur (in, out) - The current spot in the tree. Will be updated to point to the * new node. * role (in) - The role specified in the statement * attrs (in) - The attributes specified in the statement * lineno (in) - The line number * * Returns - SELint error code **********************************/ enum selint_error insert_role_attribute(struct policy_node **cur, const char *role, struct string_list *attrs, unsigned int lineno); /********************************** * cleanup_parsing * Call after all parsing is done to free up memory **********************************/ void cleanup_parsing(void); #endif selint-1.5.1/src/perm_macro.c000066400000000000000000000365241475050262500161130ustar00rootroot00000000000000/* * Copyright 2020 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "perm_macro.h" #include "color.h" #include "maps.h" #include "util.h" #include "xalloc.h" typedef uint32_t mask_t; struct perm_macro { struct perm_macro *next; char *name; mask_t mask_raw; }; static bool initialized = false; static struct perm_macro *dir_macros = NULL; static struct perm_macro *file_macros = NULL; static struct perm_macro *lnk_file_macros = NULL; static struct perm_macro *chr_file_macros = NULL; static struct perm_macro *blk_file_macros = NULL; static struct perm_macro *sock_file_macros = NULL; static struct perm_macro *fifo_file_macros = NULL; enum pm_common_file { PM_CF__EMPTY = 0u, PM_CF_IOCTL = (1u << 1), PM_CF_READ = (1u << 2), PM_CF_WRITE = (1u << 3), PM_CF_CREATE = (1u << 4), PM_CF_GETATTR = (1u << 5), PM_CF_SETATTR = (1u << 6), PM_CF_LOCK = (1u << 7), PM_CF_RELABELFROM = (1u << 8), PM_CF_RELABELTO = (1u << 9), PM_CF_APPEND = (1u << 10), PM_CF_MAP = (1u << 11), PM_CF_UNLINK = (1u << 12), PM_CF_LINK = (1u << 13), PM_CF_RENAME = (1u << 14), PM_CF_EXECUTE = (1u << 15), PM_CF_MOUNTON = (1u << 16), PM_CF_OPEN = (1u << 17), PM_CF_WATCH = (1u << 18), // dir perms PM_CF_ADDNAME = (1u << 19), PM_CF_REMOVENAME = (1u << 20), PM_CF_REPARENT = (1u << 21), PM_CF_SEARCH = (1u << 23), PM_CF_RMDIR = (1u << 24), // file perms PM_CF_EXECNOTRANS = (1u << 25), PM_CF_ENTRYPOINT = (1u << 26), // perms not covered by macros PM_CF__UNCOVERED = (1u << 31), // extended permissions // these flags contain the original raw permission flag or'ed with // flags of permissions that are reasonable extendable // e.g. open extends getattr and write extends append PM_CF_IOCTL_X = PM_CF_IOCTL, PM_CF_GETATTR_X = PM_CF_GETATTR, PM_CF_READ_X = PM_CF_READ | PM_CF_IOCTL | PM_CF_GETATTR | PM_CF_SEARCH | PM_CF_LOCK, PM_CF_LOCK_X = PM_CF_LOCK | PM_CF_GETATTR, PM_CF_APPEND_X = PM_CF_APPEND | PM_CF_GETATTR, PM_CF_WRITE_X = PM_CF_WRITE | PM_CF_IOCTL | PM_CF_GETATTR | PM_CF_APPEND | PM_CF_LOCK, PM_CF_CREATE_X = PM_CF_CREATE | PM_CF_GETATTR | PM_CF_LINK, PM_CF_SETATTR_X = PM_CF_SETATTR | PM_CF_GETATTR, PM_CF_MAP_X = PM_CF_MAP | PM_CF_IOCTL | PM_CF_GETATTR, PM_CF_UNLINK_X = PM_CF_UNLINK | PM_CF_GETATTR | PM_CF_RMDIR, PM_CF_LINK_X = PM_CF_LINK | PM_CF_GETATTR, PM_CF_RENAME_X = PM_CF_RENAME | PM_CF_GETATTR, PM_CF_OPEN_X = PM_CF_OPEN | PM_CF_GETATTR, PM_CF_EXECUTE_X = PM_CF_EXECUTE | PM_CF_READ | PM_CF_GETATTR | PM_CF_MAP, PM_CF_RELABELFROM_X = PM_CF_RELABELFROM | PM_CF_GETATTR, PM_CF_RELABELTO_X = PM_CF_RELABELTO | PM_CF_GETATTR, PM_CF_MOUNTON_X = PM_CF_MOUNTON | PM_CF_GETATTR, PM_CF_WATCH_X = PM_CF_WATCH | PM_CF_READ, PM_CF_ADDNAME_X = PM_CF_ADDNAME | PM_CF_WRITE_X, PM_CF_REMOVENAME_X = PM_CF_REMOVENAME | PM_CF_WRITE_X, PM_CF_REPARENT_X = PM_CF_REPARENT | PM_CF_GETATTR, PM_CF_SEARCH_X = PM_CF_SEARCH | PM_CF_GETATTR, PM_CF_RMDIR_X = PM_CF_RMDIR | PM_CF_UNLINK_X, PM_CF_EXECNOTRANS_X = PM_CF_EXECNOTRANS | PM_CF_EXECUTE_X, PM_CF_ENTRYPOINT_X = PM_CF_ENTRYPOINT, PM_CF__UNCOVERED_X = PM_CF__UNCOVERED, }; struct pm_ltable { const char *string; enum pm_common_file flag_raw; enum pm_common_file flag_extended; }; static const struct pm_ltable pm_ltable_common_file[] = { { "ioctl", PM_CF_IOCTL, PM_CF_IOCTL_X }, { "read", PM_CF_READ, PM_CF_READ_X }, { "write", PM_CF_WRITE, PM_CF_WRITE_X }, { "create", PM_CF_CREATE, PM_CF_CREATE_X }, { "getattr", PM_CF_GETATTR, PM_CF_GETATTR_X }, { "setattr", PM_CF_SETATTR, PM_CF_SETATTR_X }, { "lock", PM_CF_LOCK, PM_CF_LOCK_X }, { "relabelfrom", PM_CF_RELABELFROM, PM_CF_RELABELFROM_X }, { "relabelto", PM_CF_RELABELTO, PM_CF_RELABELTO_X }, { "append", PM_CF_APPEND, PM_CF_APPEND_X }, { "map", PM_CF_MAP, PM_CF_MAP_X }, { "unlink", PM_CF_UNLINK, PM_CF_UNLINK_X }, { "link", PM_CF_LINK, PM_CF_LINK_X }, { "rename", PM_CF_RENAME, PM_CF_RENAME_X }, { "execute", PM_CF_EXECUTE, PM_CF_EXECUTE_X }, { "mounton", PM_CF_MOUNTON, PM_CF_MOUNTON_X }, { "open", PM_CF_OPEN, PM_CF_OPEN_X }, { "watch", PM_CF_WATCH, PM_CF_WATCH_X }, // dir perms { "add_name", PM_CF_ADDNAME, PM_CF_ADDNAME_X }, { "remove_name", PM_CF_REMOVENAME, PM_CF_REMOVENAME_X }, { "reparent", PM_CF_REPARENT, PM_CF_REPARENT_X }, { "search", PM_CF_SEARCH, PM_CF_SEARCH_X }, { "rmdir", PM_CF_RMDIR, PM_CF_RMDIR_X }, // file perms { "execute_no_trans", PM_CF_EXECNOTRANS, PM_CF_EXECNOTRANS_X }, { "entrypoint", PM_CF_ENTRYPOINT, PM_CF_ENTRYPOINT_X }, // uncovered perms { "quotaon", PM_CF__UNCOVERED, PM_CF__UNCOVERED_X }, { "audit_access", PM_CF__UNCOVERED, PM_CF__UNCOVERED_X }, { "execmod", PM_CF__UNCOVERED, PM_CF__UNCOVERED_X }, { "watch_mount", PM_CF__UNCOVERED, PM_CF__UNCOVERED_X }, { "watch_sb", PM_CF__UNCOVERED, PM_CF__UNCOVERED_X }, { "watch_with_perm", PM_CF__UNCOVERED, PM_CF__UNCOVERED_X }, { "watch_reads", PM_CF__UNCOVERED, PM_CF__UNCOVERED_X }, }; unsigned short popcount(mask_t mask); void compute_perm_mask(const struct string_list *permissions, mask_t *mask_raw, mask_t *mask_extended); static void str_to_mask(const char *permission, mask_t *mask_raw, mask_t *mask_extended) { for (size_t i = 0; i < (sizeof pm_ltable_common_file / sizeof *pm_ltable_common_file); ++i) { if (0 == strcmp(permission, pm_ltable_common_file[i].string)) { *mask_raw |= pm_ltable_common_file[i].flag_raw; *mask_extended |= pm_ltable_common_file[i].flag_extended; return; } } const struct string_list *macro_perms = look_up_in_permmacros_map(permission); if (macro_perms) { compute_perm_mask(macro_perms, mask_raw, mask_extended); return; } // treat unknown permission as uncovered *mask_raw |= PM_CF__UNCOVERED; *mask_extended |= PM_CF__UNCOVERED; } void compute_perm_mask(const struct string_list *permissions, mask_t *mask_raw, mask_t *mask_extended) { for (; permissions; permissions = permissions->next) { str_to_mask(permissions->string, mask_raw, mask_extended); } } struct string_builder { char *mem; size_t len; size_t cap; }; static struct string_builder *sb_create(size_t init_cap) { if (init_cap == 0) { init_cap = 32; } struct string_builder *ret = xmalloc(sizeof(struct string_builder)); ret->mem = xmalloc(sizeof(char) * init_cap); ret->mem[0] = '\0'; ret->len = 0; ret->cap = init_cap; return ret; } static void sb_destroy(struct string_builder *sb) { if (sb == NULL) { return; } free(sb->mem); free(sb); } static void sb_append_strn(struct string_builder *sb, const char *str, size_t len) { while (sb->len + len + 1 > sb->cap) { sb->mem = xrealloc(sb->mem, 2 * sb->cap); sb->cap = 2 * sb->cap; } memcpy(sb->mem + sb->len, str, len); sb->len += len; sb->mem[sb->len] = '\0'; } static void sb_append_str(struct string_builder *sb, const char *str) { sb_append_strn(sb, str, strlen(str)); } static char *sb_decouple_str(struct string_builder *sb) { char *ret = sb->mem; sb->mem = NULL; sb_destroy(sb); return ret; } static char *mask_to_str(mask_t mask) { struct string_builder *sb = sb_create(0); if (mask == PM_CF__EMPTY) { sb_append_str(sb, "(none)"); return sb_decouple_str(sb); } if (mask & PM_CF__UNCOVERED) { printf("%sInternal Error%s: mask_to_str() called with unsupported permission\n", color_error(), color_reset()); sb_append_str(sb, "(unsupported perm)"); return sb_decouple_str(sb); } sb_append_str(sb, "{ "); for (size_t i = 0; i < (sizeof pm_ltable_common_file / sizeof *pm_ltable_common_file); ++i) { if (mask & pm_ltable_common_file[i].flag_raw) { sb_append_str(sb, pm_ltable_common_file[i].string); sb_append_str(sb, " "); mask &= (mask_t)(~pm_ltable_common_file[i].flag_raw); } } sb_append_str(sb, "}"); return sb_decouple_str(sb); } static char *permission_strings_matched_str(const struct string_list *permissions, mask_t mask) { struct string_builder *sb = sb_create(0); sb_append_str(sb, "{ "); for (; permissions; permissions = permissions->next) { mask_t mask_raw = 0, mask_extended = 0; str_to_mask(permissions->string, &mask_raw, &mask_extended); if ((mask_raw & mask) == mask_raw) { sb_append_str(sb, permissions->string); sb_append_str(sb, " "); } } sb_append_str(sb, "}"); return sb_decouple_str(sb); } static unsigned short permission_strings_matched_count(const struct string_list *permissions, mask_t mask) { unsigned short count = 0; for (; permissions; permissions = permissions->next) { mask_t mask_raw = 0, mask_extended = 0; str_to_mask(permissions->string, &mask_raw, &mask_extended); if ((mask_raw & mask) == mask_raw) { count++; } } return count; } unsigned short popcount(mask_t mask) { unsigned short c = 0; for (; mask != 0; mask &= mask - 1) { c++; } return c; } static void load_permission_macro(const char *name, const struct string_list *permissions) { mask_t mask_raw = PM_CF__EMPTY; mask_t mask_extended = PM_CF__EMPTY; compute_perm_mask(permissions, &mask_raw, &mask_extended); // skip macros containing uncovered permissions if (mask_raw & PM_CF__UNCOVERED) { return; } struct perm_macro **category; if (ends_with(name, strlen(name), "_dir_perms", strlen("_dir_perms"))) { category = &dir_macros; } else if (ends_with(name, strlen(name), "_lnk_file_perms", strlen("_lnk_file_perms"))) { category = &lnk_file_macros; } else if (ends_with(name, strlen(name), "_chr_file_perms", strlen("_chr_file_perms"))) { category = &chr_file_macros; } else if (ends_with(name, strlen(name), "_term_perms", strlen("_term_perms"))) { category = &chr_file_macros; } else if (ends_with(name, strlen(name), "_blk_file_perms", strlen("_blk_file_perms"))) { category = &blk_file_macros; } else if (ends_with(name, strlen(name), "_sock_file_perms", strlen("_sock_file_perms"))) { category = &sock_file_macros; } else if (ends_with(name, strlen(name), "_fifo_file_perms", strlen("_fifo_file_perms"))) { category = &fifo_file_macros; } else if (ends_with(name, strlen(name), "_file_perms", strlen("_file_perms"))) { category = &file_macros; } else { // macro for unsupported class return; } struct perm_macro *tmp = xmalloc(sizeof(struct perm_macro)); tmp->name = xstrdup(name); tmp->mask_raw = mask_raw; // first entry if (*category == NULL) { tmp->next = NULL; *category = tmp; return; } // sort the permission-macro-list ascending by number of permissions const unsigned short tmp_count_raw = popcount(tmp->mask_raw); struct perm_macro *cur = *category, *prev = NULL; for (;;) { if (tmp_count_raw < popcount(cur->mask_raw)) { if (prev == NULL) { tmp->next = cur; *category = tmp; } else { tmp->next = cur; prev->next = tmp; } return; } if (cur->next == NULL) { tmp->next = NULL; cur->next = tmp; return; } prev = cur; cur = cur->next; } } char *permmacro_check(const char *class, const struct string_list *permissions) { if (!initialized) { visit_all_in_permmacros_map(load_permission_macro); initialized = true; } const struct perm_macro *category; if (0 == strcmp(class, "dir")) { category = dir_macros; } else if (0 == strcmp(class, "file")) { category = file_macros; } else if (0 == strcmp(class, "lnk_file")) { category = lnk_file_macros; } else if (0 == strcmp(class, "chr_file")) { category = chr_file_macros; } else if (0 == strcmp(class, "blk_file")) { category = blk_file_macros; } else if (0 == strcmp(class, "sock_file")) { category = sock_file_macros; } else if (0 == strcmp(class, "fifo_file")) { category = fifo_file_macros; } else { // unsupported class return NULL; } mask_t mask_raw = PM_CF__EMPTY, mask_extended = PM_CF__EMPTY; compute_perm_mask(permissions, &mask_raw, &mask_extended); // ignore av rules containing at most one recognized permission if (popcount(mask_raw & ~PM_CF__UNCOVERED) < 2) { return NULL; } // special extending rules { // extend setattr on create AND write if (mask_extended & (PM_CF_CREATE | PM_CF_WRITE)) { mask_extended |= PM_CF_SETATTR_X; } // extend rename/reparent on create AND unlink/rmdir // (rmdir extends unlink, so PM_CF_UNLINK is set iff PM_CF_RMDIR is set) if (mask_extended & (PM_CF_CREATE | PM_CF_UNLINK)) { mask_extended |= (PM_CF_RENAME_X | PM_CF_REPARENT_X); } } const char *best_name = NULL; unsigned short best_coverage = 0; unsigned short best_extending = 0; mask_t best_mask_raw; for (const struct perm_macro *cur = category; cur; cur= cur->next) { // ignore macros covering additional non-extended permissions if (cur->mask_raw & ~mask_extended) { continue; } const unsigned short coverage = popcount(cur->mask_raw & mask_raw); // ignore macros covering only one used permission if (coverage < 2) { continue; } // ignore macros with less coverage than best yet match if (coverage < best_coverage) { continue; } const unsigned short extending = popcount(cur->mask_raw & ~mask_raw); // ignore macros with equal coverage but more extended permissions if (coverage == best_coverage && extending > best_extending) { continue; } // ignore macros replacing only one permission string, // e.g. { map read_file_perms } should not suggest mmap_read_file_perms replacing { map } // cause read_file_perms include { lock } but mmap_read_file_perms not if (permission_strings_matched_count(permissions, cur->mask_raw & mask_raw) < 2) { continue; } best_name = cur->name; best_coverage = coverage; best_mask_raw = cur->mask_raw; best_extending = extending; } // no macro match found if (!best_name) { return NULL; } // matched macro already used // we match read_file_perms for { read_file_perms }, because its the best match // and we do not discard it prior, cause we do not want to suggest search_dir_perms on list_dir_perms if (str_in_sl(best_name, permissions)) { return NULL; } char *perms_added = mask_to_str(best_mask_raw & ~mask_raw); char *perms_matched = permission_strings_matched_str(permissions, best_mask_raw & mask_raw); #define MSG_STR "Suggesting permission macro: %s (replacing %s, would add %s)" size_t len = (size_t)snprintf(NULL, 0, MSG_STR, best_name, perms_matched, perms_added); char *ret = xmalloc(len + 1); snprintf(ret, len + 1, MSG_STR, best_name, perms_matched, perms_added); #undef MSG_STR free(perms_matched); free(perms_added); return ret; } static void free_perm_macro(struct perm_macro *to_free) { while (to_free) { struct perm_macro *tmp = to_free->next; free(to_free->name); free(to_free); to_free = tmp; } } void free_permmacros(void) { initialized = false; free_perm_macro(dir_macros); free_perm_macro(file_macros); free_perm_macro(lnk_file_macros); free_perm_macro(chr_file_macros); free_perm_macro(blk_file_macros); free_perm_macro(sock_file_macros); free_perm_macro(fifo_file_macros); dir_macros = NULL; file_macros = NULL; lnk_file_macros = NULL; chr_file_macros = NULL; blk_file_macros = NULL; sock_file_macros = NULL; fifo_file_macros = NULL; } selint-1.5.1/src/perm_macro.h000066400000000000000000000025451475050262500161140ustar00rootroot00000000000000/* * Copyright 2020 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PERMMACRO_H #define PERMMACRO_H #include "string_list.h" /********************************************* * permmacro_check * Performs a check on the given class and permissions whether a declared * permission-macro can be used to simplify the used permissions. * e.g. file:{ open read } leads to read_file_perms being suggested * class (in) - The related class * permissions (in) - The currently used permissions * Returns - a string containing a message (which needs to be freed) or NULL. *********************************************/ char *permmacro_check(const char *class, const struct string_list *permissions); /********************************************* * Free internal allocations *********************************************/ void free_permmacros(void); #endif selint-1.5.1/src/runner.c000066400000000000000000000376301475050262500152770ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "color.h" #include "runner.h" #include "fc_checks.h" #include "if_checks.h" #include "te_checks.h" #include "parse_fc.h" #include "parse.h" #include "util.h" #include "startup.h" #include "xalloc.h" #define CHECK_ENABLED(cid) is_check_enabled(cid, config_enabled_checks, config_disabled_checks, cl_enabled_checks, cl_disabled_checks, only_enabled) struct policy_node *parse_one_file(const char *filename, enum node_flavor flavor) { char *copy = xstrdup(filename); char *mod_name = basename(copy); mod_name[strlen(mod_name) - 3] = '\0'; // Remove suffix set_current_module_name(mod_name); free(copy); FILE *f = fopen(filename, "re"); if (!f) { printf("%sError%s: Failed to open %s: %s\n", color_error(), color_reset(), filename, strerror(errno)); return NULL; } struct policy_node *ast = yyparse_wrapper(f, filename, flavor); fclose(f); // dont run cleanup_parsing until everything is done because it frees the maps return ast; } int is_check_enabled(const char *check_name, const struct string_list *config_enabled_checks, const struct string_list *config_disabled_checks, const struct string_list *cl_enabled_checks, const struct string_list *cl_disabled_checks, int only_enabled) { int is_enabled = check_name[0] == 'X' ? 0 : 1; // default to enabled, except for extra checks if (only_enabled) { // if only_enabled is true, we only want to enable checks that are // explicitly enabled in the cl_enabled_checks. So change the default // enabled state to disabled, and skip all other checks except for the // enabled checks. is_enabled = 0; } else { if (str_in_sl(check_name, config_disabled_checks)) { is_enabled = 0; } if (str_in_sl(check_name, config_enabled_checks)) { is_enabled = 1; } if (str_in_sl(check_name, cl_disabled_checks)) { is_enabled = 0; } } if (str_in_sl(check_name, cl_enabled_checks)) { is_enabled = 1; } return is_enabled; } struct checks *register_checks(char level, const struct string_list *config_enabled_checks, const struct string_list *config_disabled_checks, const struct string_list *cl_enabled_checks, const struct string_list *cl_disabled_checks, int only_enabled) { struct checks *ck = xmalloc(sizeof(struct checks)); memset(ck, 0, sizeof(struct checks)); if (CHECK_ENABLED("X-001")) { add_check(NODE_INTERFACE_DEF, ck, "X-001", check_unused_interface); add_check(NODE_TEMP_DEF, ck, "X-001", check_unused_interface); } if (CHECK_ENABLED("X-002")) { add_check(NODE_AV_RULE, ck, "X-002", check_excluding_av_rule); add_check(NODE_IF_CALL, ck, "X-002", check_excluding_av_rule); } switch (level) { case 'C': if (CHECK_ENABLED("C-001")) { add_check(NODE_TE_FILE, ck, "C-001", check_te_order); add_check(NODE_DECL, ck, "C-001", check_te_order); add_check(NODE_AV_RULE, ck, "C-001", check_te_order); add_check(NODE_XAV_RULE, ck, "C-001", check_te_order); add_check(NODE_IF_CALL, ck, "C-001", check_te_order); add_check(NODE_TT_RULE, ck, "C-001", check_te_order); add_check(NODE_CLEANUP, ck, "C-001", check_te_order); } if (CHECK_ENABLED("C-004")) { add_check(NODE_INTERFACE_DEF, ck, "C-004", check_interface_definitions_have_comment); add_check(NODE_TEMP_DEF, ck, "C-004", check_interface_definitions_have_comment); } if (CHECK_ENABLED("C-005")) { add_check(NODE_AV_RULE, ck, "C-005", check_unordered_perms); add_check(NODE_XAV_RULE, ck, "C-005", check_unordered_perms); add_check(NODE_DECL, ck, "C-005", check_unordered_perms); } if (CHECK_ENABLED("C-006")) { add_check(NODE_REQUIRE, ck, "C-006", check_unordered_declaration_in_require); add_check(NODE_GEN_REQ, ck, "C-006", check_unordered_declaration_in_require); } if (CHECK_ENABLED("C-007")) { add_check(NODE_AV_RULE, ck, "C-007", check_no_self); add_check(NODE_XAV_RULE, ck, "C-007", check_no_self); } if (CHECK_ENABLED("C-008")) { add_check(NODE_BOOLEAN_POLICY, ck, "C-008", check_foreign_cond_id); add_check(NODE_TUNABLE_POLICY, ck, "C-008", check_foreign_cond_id); } // FALLTHRU case 'S': if (CHECK_ENABLED("S-001")) { add_check(NODE_REQUIRE, ck, "S-001", check_require_block); add_check(NODE_GEN_REQ, ck, "S-001", check_require_block); } if (CHECK_ENABLED("S-002")) { add_check(NODE_FC_ENTRY, ck, "S-002", check_file_context_types_in_mod); } if (CHECK_ENABLED("S-003")) { add_check(NODE_SEMICOLON, ck, "S-003", check_useless_semicolon); } if (CHECK_ENABLED("S-004")) { add_check(NODE_IF_CALL, ck, "S-004", check_if_calls_template); } if (CHECK_ENABLED("S-005")) { add_check(NODE_DECL, ck, "S-005", check_decl_in_if); } if (CHECK_ENABLED("S-006")) { add_check(NODE_HEADER, ck, "S-006", check_bare_module_statement); } if (CHECK_ENABLED("S-007")) { add_check(NODE_FC_ENTRY, ck, "S-007", check_gen_context_no_range); } if (CHECK_ENABLED("S-008")) { add_check(NODE_GEN_REQ, ck, "S-008", check_unquoted_gen_require_block); } if (CHECK_ENABLED("S-009")) { add_check(NODE_AV_RULE, ck, "S-009", check_perm_macro_class_mismatch); } if (CHECK_ENABLED("S-010")) { add_check(NODE_AV_RULE, ck, "S-010", check_perm_macro_available); } if (CHECK_ENABLED("S-011")) { add_check(NODE_EMPTY, ck, "S-011", check_file_context_error_nodes); } // FALLTHRU case 'W': if (CHECK_ENABLED("W-001")) { add_check(NODE_AV_RULE, ck, "W-001", check_no_explicit_declaration); add_check(NODE_XAV_RULE, ck, "W-001", check_no_explicit_declaration); add_check(NODE_IF_CALL, ck, "W-001", check_no_explicit_declaration); add_check(NODE_TT_RULE, ck, "W-001", check_no_explicit_declaration); add_check(NODE_RT_RULE, ck, "W-001", check_no_explicit_declaration); add_check(NODE_ROLE_ALLOW, ck, "W-001", check_no_explicit_declaration); add_check(NODE_ROLE_TYPES, ck, "W-001", check_no_explicit_declaration); add_check(NODE_ALIAS, ck, "W-001", check_no_explicit_declaration); add_check(NODE_TYPE_ALIAS, ck, "W-001", check_no_explicit_declaration); add_check(NODE_TYPE_ATTRIBUTE, ck, "W-001", check_no_explicit_declaration); add_check(NODE_ROLE_ATTRIBUTE, ck, "W-001", check_no_explicit_declaration); add_check(NODE_PERMISSIVE, ck, "W-001", check_no_explicit_declaration); add_check(NODE_DECL, ck, "W-001", check_no_explicit_declaration); } if (CHECK_ENABLED("W-002")) { add_check(NODE_AV_RULE, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_XAV_RULE, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_IF_CALL, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_TT_RULE, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_RT_RULE, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_ROLE_ALLOW, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_ROLE_TYPES, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_ALIAS, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_TYPE_ALIAS, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_TYPE_ATTRIBUTE, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_ROLE_ATTRIBUTE, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_PERMISSIVE, ck, "W-002", check_name_used_but_not_required_in_if); add_check(NODE_DECL, ck, "W-002", check_name_used_but_not_required_in_if); } if (CHECK_ENABLED("W-003")) { add_check(NODE_DECL, ck, "W-003", check_name_required_but_not_used_in_if); } if (CHECK_ENABLED("W-004")) { add_check(NODE_FC_ENTRY, ck, "W-004", check_file_context_regex); } if (CHECK_ENABLED("W-005")) { add_check(NODE_IF_CALL, ck, "W-005", check_module_if_call_in_optional); } if (CHECK_ENABLED("W-006")) { add_check(NODE_IF_CALL, ck, "W-006", check_empty_if_call_arg); } if (CHECK_ENABLED("W-007")) { add_check(NODE_IF_CALL, ck, "W-007", check_space_if_call_arg); } if (CHECK_ENABLED("W-008")) { add_check(NODE_AV_RULE, ck, "W-008", check_risky_allow_perm); } if (CHECK_ENABLED("W-009")) { add_check(NODE_HEADER, ck, "W-009", check_module_file_name_mismatch); } if (CHECK_ENABLED("W-010")) { add_check(NODE_IF_CALL, ck, "W-010", check_unknown_interface_call); } if (CHECK_ENABLED("W-011")) { add_check(NODE_DECL, ck, "W-011", check_required_declaration_own); } if (CHECK_ENABLED("W-012")) { add_check(NODE_BOOLEAN_POLICY, ck, "W-012", check_unknown_cond_id); add_check(NODE_TUNABLE_POLICY, ck, "W-012", check_unknown_cond_id); } if (CHECK_ENABLED("W-013")) { add_check(NODE_AV_RULE, ck, "W-013", check_audit_access_perm); } // FALLTHRU case 'E': if (CHECK_ENABLED("E-002")) { add_check(NODE_ERROR, ck, "E-002", check_file_context_error_nodes); } if (CHECK_ENABLED("E-003")) { add_check(NODE_FC_ENTRY, ck, "E-003", check_file_context_users); } if (CHECK_ENABLED("E-004")) { add_check(NODE_FC_ENTRY, ck, "E-004", check_file_context_roles); } if (CHECK_ENABLED("E-005")) { add_check(NODE_FC_ENTRY, ck, "E-005", check_file_context_types_exist); } if (CHECK_ENABLED("E-006")) { add_check(NODE_DECL, ck, "E-006", check_declaration_interface_nameclash); } if (CHECK_ENABLED("E-007") && check_unknown_permission_condition()) { add_check(NODE_AV_RULE, ck, "E-007", check_unknown_permission); } if (CHECK_ENABLED("E-008") && check_unknown_class_condition()) { add_check(NODE_AV_RULE, ck, "E-008", check_unknown_class); add_check(NODE_RT_RULE, ck, "E-008", check_unknown_class); add_check(NODE_TT_RULE, ck, "E-008", check_unknown_class); } if (CHECK_ENABLED("E-009")) { add_check(NODE_OPTIONAL_POLICY, ck, "E-009", check_empty_block); add_check(NODE_GEN_REQ, ck, "E-009", check_empty_block); add_check(NODE_REQUIRE, ck, "E-009", check_empty_block); } if (CHECK_ENABLED("E-010")) { add_check(NODE_M4_SIMPLE_MACRO, ck, "E-010", check_stray_word); } // FALLTHRU case 'F': break; default: free(ck); return NULL; } return ck; } enum selint_error parse_all_files_in_list(struct policy_file_list *files, enum node_flavor flavor) { struct policy_file_node *current = files->head; while (current) { print_if_verbose("Parsing %s\n", current->file->filename); current->file->ast = parse_one_file(current->file->filename, flavor); if (!current->file->ast) { return SELINT_PARSE_ERROR; } current = current->next; } return SELINT_SUCCESS; } enum selint_error parse_all_fc_files_in_list(struct policy_file_list *files, const struct string_list *custom_fc_macros) { struct policy_file_node *current = files->head; while (current) { print_if_verbose("Parsing fc file %s\n", current->file->filename); current->file->ast = parse_fc_file(current->file->filename, custom_fc_macros); if (!current->file->ast) { return SELINT_PARSE_ERROR; } current = current->next; } return SELINT_SUCCESS; } enum selint_error run_checks_on_one_file(struct checks *ck, const struct check_data *data, const struct policy_node *head) { const struct policy_node *current = head; while (current) { enum selint_error res = call_checks(ck, data, current); if (res != SELINT_SUCCESS) { return res; } current = dfs_next(current); } // Give checks a change to clean up state struct policy_node cleanup; memset(&cleanup, 0, sizeof(struct policy_node)); cleanup.flavor = NODE_CLEANUP; return call_checks(ck, data, &cleanup); } enum selint_error run_all_checks(struct checks *ck, enum file_flavor flavor, struct policy_file_list *files, const struct config_check_data *ccd) { struct policy_file_node *file = files->head; struct check_data data; data.flavor = flavor; while (file) { { char *copy = xstrdup(file->file->filename); data.filename = xstrdup(basename(copy)); free(copy); } data.mod_name = xstrdup(data.filename); data.filepath = file->file->filename; data.config_check_data = ccd; char *suffix_ptr = strrchr(data.mod_name, '.'); *suffix_ptr = '\0'; enum selint_error res = run_checks_on_one_file(ck, &data, file->file->ast); if (res != SELINT_SUCCESS) { return res; } free(data.filename); free(data.mod_name); file = file->next; } return SELINT_SUCCESS; } enum selint_error run_analysis(struct checks *ck, struct policy_file_list *te_files, struct policy_file_list *if_files, struct policy_file_list *fc_files, struct policy_file_list *context_te_files, struct policy_file_list *context_if_files, const struct string_list *custom_fc_macros, const struct config_check_data *ccd) { enum selint_error res; res = parse_all_files_in_list(if_files, NODE_IF_FILE); if (res != SELINT_SUCCESS) { goto out; } // We parse all the context files for the side effects of parsing (populating // the hash tables), and to mark the transform interfaces. Then we only // run checks on the non-context files res = parse_all_files_in_list(context_if_files, NODE_IF_FILE); if (res != SELINT_SUCCESS) { goto out; } // Make temporary joined list to mark ALL transform interfaces struct policy_file_list *all_if_files = xcalloc(1, sizeof(struct policy_file_list)); if (if_files->tail) { // Only concatenate if if_files contains files all_if_files->head = if_files->head; if_files->tail->next = context_if_files->head; } else { // If both are empty, just having an empty list is fine all_if_files->head = context_if_files->head; } all_if_files->tail = context_if_files->tail; mark_transform_interfaces(all_if_files); // Restore if (if_files->tail) { if_files->tail->next = NULL; } free(all_if_files); res = parse_all_files_in_list(context_te_files, NODE_TE_FILE); if (res != SELINT_SUCCESS) { goto out; } res = parse_all_files_in_list(te_files, NODE_TE_FILE); if (res != SELINT_SUCCESS) { goto out; } res = parse_all_fc_files_in_list(fc_files, custom_fc_macros); if (res != SELINT_SUCCESS) { goto out; } res = run_all_checks(ck, FILE_TE_FILE, te_files, ccd); if (res != SELINT_SUCCESS) { goto out; } res = run_all_checks(ck, FILE_IF_FILE, if_files, ccd); if (res != SELINT_SUCCESS) { goto out; } res = run_all_checks(ck, FILE_FC_FILE, fc_files, ccd); if (res != SELINT_SUCCESS) { goto out; } out: cleanup_parsing(); return res; } void display_run_summary(const struct checks *ck) { printf("Found the following issue counts:\n"); display_check_issue_counts(ck); } selint-1.5.1/src/runner.h000066400000000000000000000140051475050262500152730ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RUNNER_H #define RUNNER_H #include "check_hooks.h" #include "selint_error.h" #include "parse_functions.h" #include "file_list.h" /**************************************************** * Parse a policy file * filename - The name of the files to parse. * flavor - The node type corresponding to the type of file (TE or IF) * Returns the head of the parsed AST or NULL on failure ****************************************************/ struct policy_node *parse_one_file(const char *filename, enum node_flavor flavor); /**************************************************** * Determine whether a specific check is enabled based on the * config file and the command line arguments ****************************************************/ int is_check_enabled(const char *check_name, const struct string_list *config_enabled_checks, const struct string_list *config_disabled_checks, const struct string_list *cl_enabled_checks, const struct string_list *cl_disabled_checks, int only_enabled); /**************************************************** * Allocate and populate a checks structure with the list of checks enabled for * this run. Caller is responsible for freeing * level - The severity level to load checks at and above * Returns the allocated checks structure or NULL on failure ****************************************************/ struct checks *register_checks(char level, const struct string_list *config_enabled_checks, const struct string_list *config_disabled_checks, const struct string_list *cl_enabled_checks, const struct string_list *cl_disabled_checks, int only_enabled); /**************************************************** * Parse all the provided te or if files, storing their parsed ASTs * in the provided list * files - The files to parse. This list is updated with parsed ASTs * flavor - The node type corresponding to the sorts of files in this list * Returns SELINT_SUCCESS on success or an error code ****************************************************/ enum selint_error parse_all_files_in_list(struct policy_file_list *files, enum node_flavor flavor); /**************************************************** * Parse all the provided fc files, storing their parsed ASTs * in the provided list * files - The files to parse. This list is updated with parsed ASTs * Returns SELINT_SUCCESS on success or an error code ****************************************************/ enum selint_error parse_all_fc_files_in_list(struct policy_file_list *files, const struct string_list *custom_fc_macros); /**************************************************** * Run all checks for a certain file * ck - The checks structure * data - metadata about the file * head - The head of the AST for that file * Returns SELINT_SUCCESS on success or an error code ****************************************************/ enum selint_error run_checks_on_one_file(struct checks *ck, const struct check_data *data, const struct policy_node *head); /**************************************************** * Run all checks on all files of a certain type (te, if or fc) * ck - The checks structure * flavor - The type of file to check * files - The list of files of that type to check * Returns SELINT_SUCCESS on success or an error code ****************************************************/ enum selint_error run_all_checks(struct checks *ck, enum file_flavor flavor, struct policy_file_list *files, const struct config_check_data *ccd); /**************************************************** * Run the complete analysis, checking all files and reporting results * ck - The checks structure * te_files - The list of te files to check * if_files - The list of if files to check * fc_files - The list of fc files to check * context_te_files - Additional te files to parse, but not scan. This is used * to load symbols (eg type names) that may be referenced in scanned files. * context_if_files - Additional if files to parse, but not scan. This is used * to load interface/template names and contents to do analysis on how they are * used in scanned files. * custom_fc_macros - Custom macros used in fc files defined in config * ccd - Information loaded from the config to be given to checks * Returns SELINT_SUCCESS on success or an error code ****************************************************/ enum selint_error run_analysis(struct checks *ck, struct policy_file_list *te_files, struct policy_file_list *if_files, struct policy_file_list *fc_files, struct policy_file_list *context_te_files, struct policy_file_list *context_if_files, const struct string_list *custom_fc_macros, const struct config_check_data *ccd); /**************************************************** * Display a summary of the analysis that was just run * ck - The checks structure * no return ****************************************************/ void display_run_summary(const struct checks *ck); #endif selint-1.5.1/src/selint_config.c000066400000000000000000000207341475050262500166060ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "util.h" #include "selint_config.h" #define READ_STRING_LIST_FROM_CONFIG(slp, config_name) \ if (slp) { \ struct string_list *end = NULL; \ for (unsigned int i = 0; i < cfg_size(cfg, config_name); i++) { \ struct string_list *cur = sl_from_str(cfg_getnstr(cfg, config_name, i)); \ if (!end) { \ *slp = end = cur; \ } else { \ end->next = cur; \ end = end->next; \ } \ } \ } \ static enum selint_error parse_bool(const char *string, bool *value) { if (0 == strcmp("true", string) || 0 == strcmp("True", string) || 0 == strcmp("TRUE", string) || 0 == strcmp("yes", string) || 0 == strcmp("Yes", string) || 0 == strcmp("YES", string)) { *value = true; return SELINT_SUCCESS; } if (0 == strcmp("false", string) || 0 == strcmp("False", string) || 0 == strcmp("FALSE", string) || 0 == strcmp("no", string) || 0 == strcmp("No", string) || 0 == strcmp("NO", string)) { *value = false; return SELINT_SUCCESS; } return SELINT_CONFIG_PARSE_ERROR; } static void insert_config_declarations(cfg_t * cfg, const char *config_item, enum decl_flavor flavor) { for (unsigned int i = 0; i < cfg_size(cfg, config_item); i++) { insert_into_decl_map(cfg_getnstr(cfg, config_item, i), "__assumed__", flavor); } } enum selint_error parse_config(const char *config_filename, int in_source_mode, char *severity, struct string_list **config_disabled_checks, struct string_list **config_enabled_checks, struct string_list **custom_fc_macros, struct config_check_data *config_check_data) { IGNORE_CONST_DISCARD_BEGIN; cfg_opt_t opts[] = { CFG_STR("severity", "convention", CFGF_NONE), CFG_STR_LIST("disable", "{}", CFGF_NONE), CFG_STR_LIST("enable_normal", "{}", CFGF_NONE), CFG_STR_LIST("enable_source", "{}", CFGF_NONE), CFG_STR_LIST("assume_users", "{}", CFGF_NONE), CFG_STR_LIST("assume_roles", "{}", CFGF_NONE), CFG_STR_LIST("custom_fc_macros", "{}", CFGF_NONE), CFG_STR_LIST("custom_te_simple_macros", "{}", CFGF_NONE), CFG_STR("ordering_rules", "refpolicy-lax", CFGF_NONE), CFG_STR_LIST("ordering_requires", "{ bool, class, role, attribute_role, attribute, type }", CFGF_NONE), CFG_STR("ordering_requires_same_flavor", "true", CFGF_NONE), CFG_STR("skip_checking_generated_fcs", "true", CFGF_NONE), CFG_END() }; IGNORE_CONST_DISCARD_END; cfg_t *cfg; cfg = cfg_init(opts, CFGF_NONE); print_if_verbose("Loading configuration from: %s\n", config_filename); if (cfg_parse(cfg, config_filename) == CFG_PARSE_ERROR) { printf ("Parse error when attempting to parse configuration file.\n"); cfg_free(cfg); return SELINT_CONFIG_PARSE_ERROR; } // Not specified on command line. Read from config const char *config_severity = cfg_getstr(cfg, "severity"); if (strcmp(config_severity, "convention") == 0) { *severity = 'C'; } else if (strcmp(config_severity, "style") == 0) { *severity = 'S'; } else if (strcmp(config_severity, "warning") == 0) { *severity = 'W'; } else if (strcmp(config_severity, "error") == 0) { *severity = 'E'; } else if (strcmp(config_severity, "fatal") == 0) { *severity = 'F'; } else { printf ("Invalid severity level (%s) specified in config.\n"\ "Options are \"convention\", \"style\", \"warning\", \"error\" and \"fatal\"\n", config_severity); cfg_free(cfg); return SELINT_CONFIG_PARSE_ERROR; } READ_STRING_LIST_FROM_CONFIG(config_disabled_checks, "disable") if (in_source_mode) { READ_STRING_LIST_FROM_CONFIG(config_enabled_checks, "enable_source") } else { READ_STRING_LIST_FROM_CONFIG(config_enabled_checks, "enable_normal") } insert_config_declarations(cfg, "assume_users", DECL_USER); insert_config_declarations(cfg, "assume_roles", DECL_ROLE); READ_STRING_LIST_FROM_CONFIG(custom_fc_macros, "custom_fc_macros"); READ_STRING_LIST_FROM_CONFIG(&(config_check_data->custom_te_simple_macros), "custom_te_simple_macros"); const char *config_ordering_rules = cfg_getstr(cfg, "ordering_rules"); if (strcmp(config_ordering_rules, "refpolicy") == 0) { config_check_data->order_conf = ORDER_REF; } else if (strcmp(config_ordering_rules, "refpolicy-light") == 0) { config_check_data->order_conf = ORDER_LIGHT; } else if (strcmp(config_ordering_rules, "refpolicy-lax") == 0) { config_check_data->order_conf = ORDER_LAX; } else { printf("Invalid ordering rules (%s) specified in config.\n"\ "Options are \"refpolicy\", \"refpolicy-light\"\n"\ "and \"refpolicy-lax\"\n", config_ordering_rules); cfg_free(cfg); return SELINT_CONFIG_PARSE_ERROR; } // ordering_requires const unsigned count = cfg_size(cfg, "ordering_requires"); if (count != 6) { printf("Incorrect amount of ordering_requires flavors (%u) specified in config.\n"\ "The required amount is 6.\n", count); cfg_free(cfg); return SELINT_CONFIG_PARSE_ERROR; } for (unsigned i = 0; i < 6; ++i) { const char *cfg_flavor = cfg_getnstr(cfg, "ordering_requires", i); enum decl_flavor parsed_flavor; if (0 == strcmp("attribute", cfg_flavor)) { parsed_flavor = DECL_ATTRIBUTE; } else if (0 == strcmp("attribute_role", cfg_flavor)) { parsed_flavor = DECL_ATTRIBUTE_ROLE; } else if (0 == strcmp("bool", cfg_flavor)) { parsed_flavor = DECL_BOOL; } else if (0 == strcmp("class", cfg_flavor)) { parsed_flavor = DECL_CLASS; } else if (0 == strcmp("role", cfg_flavor)) { parsed_flavor = DECL_ROLE; } else if (0 == strcmp("type", cfg_flavor)) { parsed_flavor = DECL_TYPE; } else { printf("Invalid ordering_requires flavor (%s) specified in config.\n"\ "See configuration file for available options\n", cfg_flavor); cfg_free(cfg); return SELINT_CONFIG_PARSE_ERROR; } for (unsigned j = 0; j < i; ++j) { if (config_check_data->order_requires[j] == parsed_flavor) { printf("Duplicate ordering_requires flavor (%s) specified in config.\n", cfg_flavor); cfg_free(cfg); return SELINT_CONFIG_PARSE_ERROR; } } config_check_data->order_requires[i] = parsed_flavor; } // ordering_requires_same_flavor const char *config_ordering_requires_same_flavor = cfg_getstr(cfg, "ordering_requires_same_flavor"); bool ordering_requires_same_flavor; enum selint_error r = parse_bool(config_ordering_requires_same_flavor, &ordering_requires_same_flavor); if (r != SELINT_SUCCESS) { printf("Invalid ordering_requires_same_flavor setting (%s) specified in config.\n"\ "Options are \"true\" and \"false\"\n", config_ordering_requires_same_flavor); cfg_free(cfg); return r; } config_check_data->ordering_requires_same_flavor = ordering_requires_same_flavor; // skip_checking_generated_fcs const char *config_skip_checking_generated_fcs = cfg_getstr(cfg, "skip_checking_generated_fcs"); bool skip_checking_generated_fcs; r = parse_bool(config_skip_checking_generated_fcs, &skip_checking_generated_fcs); if (r != SELINT_SUCCESS) { printf("Invalid skip_checking_generated_fcs setting (%s) specified in config.\n"\ "Options are \"true\" and \"false\"\n", config_skip_checking_generated_fcs); cfg_free(cfg); return r; } config_check_data->skip_checking_generated_fcs = skip_checking_generated_fcs; cfg_free(cfg); return SELINT_SUCCESS; } void free_selint_config(struct config_check_data *config_check_data) { if (!config_check_data) { return; } free_string_list(config_check_data->custom_te_simple_macros); } selint-1.5.1/src/selint_config.h000066400000000000000000000034031475050262500166050ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CONFIG_H #define CONFIG_H #include #include "selint_error.h" #include "string_list.h" #include "tree.h" #include "maps.h" enum order_conf { ORDER_REF, ORDER_LIGHT, ORDER_LAX }; struct config_check_data { enum order_conf order_conf; enum decl_flavor order_requires[6]; bool ordering_requires_same_flavor; bool skip_checking_generated_fcs; struct string_list *custom_te_simple_macros; }; /******************************************************************* * Parse the config file and set the function arguments appropriately * Return SELINT_SUCCESS or error code ********************************************************************/ enum selint_error parse_config(const char *config_filename, int in_source_mode, char *severity, struct string_list **config_disabled_checks, struct string_list **config_enabled_checks, struct string_list **custom_fc_macros, struct config_check_data *config_check_data); void free_selint_config(struct config_check_data *config_check_data); #endif selint-1.5.1/src/selint_error.h000066400000000000000000000015571475050262500165010ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SELINT_ERROR_H #define SELINT_ERROR_H enum selint_error { SELINT_SUCCESS, SELINT_BAD_ARG, SELINT_OUT_OF_MEM, SELINT_NO_MOD_NAME, SELINT_NOT_IN_BLOCK, SELINT_IF_CALL_LOOP, SELINT_PARSE_ERROR, SELINT_M4_SUB_FAILURE, SELINT_CONFIG_PARSE_ERROR, SELINT_IO_ERROR }; #endif selint-1.5.1/src/startup.c000066400000000000000000000177311475050262500154700ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "startup.h" #include "color.h" #include "maps.h" #include "parse.h" #include "parse_functions.h" #include "tree.h" #include "util.h" #include "xalloc.h" enum selint_error load_access_vectors_kernel(const char *av_path) { /* check if av_path really exists, * e.g. checking nonexistent /sys/fs/selinux/class * on a SELinux disabled system */ if (access(av_path, F_OK) != 0) { return SELINT_IO_ERROR; } enum selint_error r = SELINT_PARSE_ERROR; char *av_path_copy = xstrdup(av_path); char *const paths[2] = { av_path_copy, NULL }; FTS *ftsp = fts_open(paths, FTS_PHYSICAL, NULL); const FTSENT *file = fts_read(ftsp); while (file) { if (file->fts_level != 0 && file->fts_info == FTS_D && 0 != strcmp(file->fts_name, "perms")) { // Directory being visited the first time insert_into_decl_map(file->fts_name, "class", DECL_CLASS); } else if (file->fts_info == FTS_F && 0 != strcmp(file->fts_name, "index")) { // File insert_into_decl_map(file->fts_name, "perm", DECL_PERM); r = SELINT_SUCCESS; } file = fts_read(ftsp); } fts_close(ftsp); free(av_path_copy); return r; } enum selint_error load_access_vectors_source(const char *av_path) { print_if_verbose("Parsing access_vector file %s\n", av_path); set_current_module_name(av_path); FILE *f = fopen(av_path, "re"); if (!f) { printf("%sError%s: Failed to open %s: %s\n", color_error(), color_reset(), av_path, strerror(errno)); return SELINT_IO_ERROR; } struct policy_node *ast = yyparse_wrapper(f, av_path, NODE_AV_FILE); fclose(f); if (!ast) { return SELINT_PARSE_ERROR; } free_policy_node(ast); return SELINT_SUCCESS; } enum selint_error load_security_classes_source(const char *sec_class_path) { FILE *fd = fopen(sec_class_path, "r"); if (!fd) { return SELINT_IO_ERROR; } enum selint_error ret = SELINT_SUCCESS; char *line = NULL; ssize_t len_read = 0; size_t buf_len = 0; while ((len_read = getline(&line, &buf_len, fd)) != -1) { if (len_read <= 1 || line[0] == '#') { continue; } const char *pos = line; if (0 != strncmp(pos, "class", strlen("class"))) { ret = SELINT_PARSE_ERROR; break; } pos += strlen("class"); if (*pos != ' ' && *pos != '\t') { ret = SELINT_PARSE_ERROR; break; } while (*pos == ' ' || *pos == '\t') { pos++; } if (!isalnum((unsigned char)*pos)) { ret = SELINT_PARSE_ERROR; break; } const char *name_start = pos; while (*pos == '_' || isalnum((unsigned char)*pos)) { pos++; } const char *name_end = pos; while (isspace((unsigned char)*pos)) { pos++; } if (*pos != '\0' && *pos != '#') { ret = SELINT_PARSE_ERROR; break; } if (*pos == '#' && NULL != strstr(pos, "userspace")) { char *name = strndup(name_start, (size_t)(name_end - name_start)); mark_userspace_class(name); free(name); } } free(line); fclose(fd); return ret; } void load_modules_normal(void) { } static int is_space(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } static char *strip_space(char *str) { while (is_space(*str)) { str++; } char *end = str; while (!is_space(*end)) { end++; } *end = '\0'; return str; } enum selint_error load_modules_source(const char *modules_conf_path) { FILE *fd = fopen(modules_conf_path, "re"); if (!fd) { return SELINT_IO_ERROR; } char *line = NULL; ssize_t len_read = 0; size_t buf_len = 0; while ((len_read = getline(&line, &buf_len, fd)) != -1) { if (len_read <= 1 || line[0] == '#') { continue; } char *pos = line; while (*pos != '\0' && is_space(*pos)) { pos++; } if (pos[0] == '#' || pos[0] == '\0') { continue; } pos = strtok(line, "="); if (!pos) { free(line); fclose(fd); return SELINT_PARSE_ERROR; } const char *mod_name = strip_space(pos); pos = strtok(NULL, "="); if (!pos) { free(line); fclose(fd); return SELINT_PARSE_ERROR; } const char *status = strip_space(pos); insert_into_mods_map(mod_name, status); if (strtok(NULL, "=")) { free(line); fclose(fd); return SELINT_PARSE_ERROR; } } free(line); fclose(fd); return SELINT_SUCCESS; } enum selint_error load_obj_perm_sets_source(const char *obj_perm_sets_path) { FILE *f = fopen(obj_perm_sets_path, "re"); if (!f) { return SELINT_IO_ERROR; } struct policy_node *ast = yyparse_wrapper(f, obj_perm_sets_path, NODE_SPT_FILE); fclose(f); if (ast == NULL) { return SELINT_PARSE_ERROR; } free_policy_node(ast); return SELINT_SUCCESS; } static int mark_transform_interfaces_one_file(const struct policy_node *ast) { int marked_transform = 0; const struct policy_node *cur = ast; while (cur) { if (cur->flavor == NODE_INTERFACE_DEF && cur->first_child && !is_transform_if(cur->data.str)) { const struct policy_node *child = cur->first_child; while (child && (child->flavor == NODE_START_BLOCK || child->flavor == NODE_REQUIRE || child->flavor == NODE_GEN_REQ)) { child = child->next; } if (!child) { // Nothing in interface besides possibly require cur = dfs_next(cur); continue; } if (child->flavor == NODE_IF_CALL) { if (is_transform_if(child->data.ic_data->name)) { mark_transform_if(cur->data.str); marked_transform = 1; } } } cur = dfs_next(cur); } return marked_transform; } enum selint_error load_devel_headers(struct policy_file_list *context_files) { char header_loc[] = "/usr/share/selinux/devel"; char *const paths[2] = { header_loc, NULL }; FTS *ftsp = fts_open(paths, FTS_PHYSICAL | FTS_NOSTAT, NULL); const FTSENT *file = fts_read(ftsp); while (file) { const char *suffix = (file->fts_pathlen > 3) ? (file->fts_path + file->fts_pathlen - 3) : NULL; if (suffix && !strcmp(suffix, ".if")) { file_list_push_back(context_files, make_policy_file(file->fts_path, NULL)); char *mod_name = xstrdup(file->fts_name); mod_name[file->fts_namelen - 3] = '\0'; insert_into_mod_layers_map(mod_name, file->fts_parent->fts_name); free(mod_name); } file = fts_read(ftsp); } fts_close(ftsp); return SELINT_SUCCESS; } static enum selint_error load_global_conditions_file(const char *path) { print_if_verbose("Parsing global conditions file %s\n", path); set_current_module_name("__global__"); FILE *f = fopen(path, "re"); if (!f) { printf("%sError%s: Failed to open file %s: %s\n", color_error(), color_reset(), path, strerror(errno)); return SELINT_IO_ERROR; } struct policy_node *ast = yyparse_wrapper(f, path, NODE_COND_FILE); fclose(f); if (!ast) { return SELINT_PARSE_ERROR; } free_policy_node(ast); return SELINT_SUCCESS; } enum selint_error load_global_conditions(const struct string_list *paths) { for (const struct string_list *p = paths; p; p = p->next) { enum selint_error rc = load_global_conditions_file(p->string); if (rc != SELINT_SUCCESS) { return rc; } } return SELINT_SUCCESS; } enum selint_error mark_transform_interfaces(const struct policy_file_list *files) { const struct policy_file_node *cur; int marked_transform; do { marked_transform = 0; cur = files->head; while (cur) { marked_transform = marked_transform || mark_transform_interfaces_one_file(cur->file->ast); cur = cur->next; } } while (marked_transform); return SELINT_SUCCESS; } selint-1.5.1/src/startup.h000066400000000000000000000025011475050262500154620ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef STARTUP_H #define STARTUP_H #include "selint_error.h" #include "file_list.h" #include "string_list.h" enum selint_error load_access_vectors_kernel(const char *av_path); enum selint_error load_access_vectors_source(const char *av_path); enum selint_error load_security_classes_source(const char *sec_class_path); void load_modules_normal(void); enum selint_error load_modules_source(const char *modules_conf_path); enum selint_error load_obj_perm_sets_source(const char *obj_perm_sets_path); enum selint_error load_devel_headers(struct policy_file_list *context_files); enum selint_error load_global_conditions(const struct string_list *paths); enum selint_error mark_transform_interfaces(const struct policy_file_list *files); #endif selint-1.5.1/src/string_list.c000066400000000000000000000060531475050262500163220ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "string_list.h" #include "xalloc.h" int str_in_sl(const char *str, const struct string_list *sl) { if (!sl) { return 0; } while (sl) { if (0 == strcmp(sl->string, str)) { return 1; } sl = sl->next; } return 0; } struct string_list *copy_string_list(const struct string_list *sl) { if (!sl) { return NULL; } struct string_list *ret = xmalloc(sizeof(struct string_list)); struct string_list *cur = ret; while (sl) { cur->string = xstrdup(sl->string); cur->has_incorrect_space = sl->has_incorrect_space; cur->arg_start = sl->arg_start; if (sl->next) { cur->next = xmalloc(sizeof(struct string_list)); } else { cur->next = NULL; } sl = sl->next; cur = cur->next; } return ret; } struct string_list *sl_from_str(const char *string) { struct string_list *ret = xmalloc(sizeof(struct string_list)); ret->string = xstrdup(string); ret->next = NULL; ret->has_incorrect_space = 0; ret->arg_start = 0; return ret; } struct string_list *sl_from_strn(const char *string, size_t len) { struct string_list *ret = xmalloc(sizeof(struct string_list)); ret->string = xstrndup(string, len); ret->next = NULL; ret->has_incorrect_space = 0; ret->arg_start = 0; return ret; } struct string_list *sl_from_str_consume(char *string) { struct string_list *ret = xmalloc(sizeof(struct string_list)); ret->string = string; ret->next = NULL; ret->has_incorrect_space = 0; ret->arg_start = 0; return ret; } struct string_list *sl_from_strs(int count, ...) { struct string_list *ret = NULL; va_list args; va_start(args, count); for (int i = 0; i < count; ++i) { ret = concat_string_lists(ret, sl_from_str(va_arg(args, const char *))); } va_end(args); return ret; } struct string_list *concat_string_lists(struct string_list *head, struct string_list *tail) { if (!head) { return tail; } if (!tail) { return head; } struct string_list *cur = head; while (cur->next) { cur = cur->next; } cur->next = tail; return head; } enum selint_error append_to_sl(struct string_list *sl, const char *string) { if (!sl) { return SELINT_BAD_ARG; } while (sl->next) { sl = sl->next; } sl->next = sl_from_str(string); return SELINT_SUCCESS; } void free_string_list(struct string_list *list) { if (list == NULL) { return; } struct string_list *cur = list; while (cur) { struct string_list *to_free = cur; cur = cur->next; free(to_free->string); free(to_free); } } selint-1.5.1/src/string_list.h000066400000000000000000000037271475050262500163340ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef STRING_LIST_H #define STRING_LIST_H #include #include #include "selint_error.h" struct string_list { char *string; struct string_list *next; uint8_t has_incorrect_space; uint8_t arg_start; }; int str_in_sl(const char *str, const struct string_list *sl); // Return an identical copy of sl struct string_list *copy_string_list(const struct string_list *sl); // Return a string list with a copy of the given string as single item struct string_list *sl_from_str(const char *string); struct string_list *sl_from_strn(const char *string, size_t len); // Return a string list with the given string as single item // Takes ownership of the given string struct string_list *sl_from_str_consume(char *string); // Return a string list with copies of the given strings struct string_list *sl_from_strs(int count, ...); // Concat two string lists, accepts NULL lists. // Note: freeing the returned list will free both original string lists struct string_list *concat_string_lists(struct string_list *head, struct string_list *tail); // Append a copy of a string on the end of the given string list // Note: this traverses the string list. In performance critical // situations with multiple appends, you should save a pointer to the end of the list enum selint_error append_to_sl(struct string_list *sl, const char *string); void free_string_list(struct string_list *list); #endif selint-1.5.1/src/te_checks.c000066400000000000000000000755561475050262500157270ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "color.h" #include "te_checks.h" #include "maps.h" #include "tree.h" #include "ordering.h" #include "util.h" #include "perm_macro.h" #include "xalloc.h" struct check_result *check_excluding_av_rule(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->flavor == NODE_AV_RULE) { // ignore neverallowallow rules if (node->data.av_data->flavor == AV_RULE_NEVERALLOW) { return NULL; } for (const struct string_list *sources = node->data.av_data->sources; sources; sources = sources->next) { if (sources->string[0] == '-') { return make_check_result('X', X_ID_EXCL_AV, "AV rule with excluded source"); } } for (const struct string_list *targets = node->data.av_data->targets; targets; targets = targets->next) { if (targets->string[0] == '-') { return make_check_result('X', X_ID_EXCL_AV, "AV rule with excluded target"); } } return NULL; } if (node->flavor == NODE_IF_CALL) { const struct string_list *args = node->data.ic_data->args; bool check_current = false; for (; args; args = args->next) { // only check if the first section argument is a type or attribute if (args->arg_start) { check_current = look_up_in_decl_map(args->string, DECL_TYPE) || look_up_in_decl_map(args->string, DECL_ATTRIBUTE); continue; } if (check_current && args->string[0] == '-' && (look_up_in_decl_map(&args->string[1], DECL_TYPE) || look_up_in_decl_map(&args->string[1], DECL_ATTRIBUTE))) { return make_check_result('X', X_ID_EXCL_AV, "Interface or macro call with excluded type"); } } return NULL; } return alloc_internal_error("Invalid node type for `check_excluding_av_rule`"); } struct check_result *check_te_order(const struct check_data *data, const struct policy_node *node) { if (!data || !data->config_check_data) { return alloc_internal_error("Uninitialized data given to C-001"); } if (data->flavor != FILE_TE_FILE) { return NULL; } static struct ordering_metadata *order_data; static unsigned int order_node_arr_index; switch (node->flavor) { case NODE_TE_FILE: order_data = prepare_ordering_metadata(data, node); order_node_arr_index = 0; if (!order_data) { return alloc_internal_error("Failed to initialize ordering for C-001"); } switch (data->config_check_data->order_conf) { case ORDER_REF: calculate_longest_increasing_subsequence(node, order_data, compare_nodes_refpolicy); break; case ORDER_LIGHT: calculate_longest_increasing_subsequence(node, order_data, compare_nodes_refpolicy_light); break; case ORDER_LAX: calculate_longest_increasing_subsequence(node, order_data, compare_nodes_refpolicy_lax); break; default: return alloc_internal_error("Unknown ordering configuration given to C-001"); } break; case NODE_CLEANUP: free_ordering_metadata(order_data); order_data = NULL; break; default: if (!order_data) { return alloc_internal_error("Ordering data was not generated for C-001"); } for (unsigned int i=order_node_arr_index; i < order_data->order_node_len; i++) { if (order_data->nodes[i].node == node) { order_node_arr_index = i; if (order_data->nodes[i].in_order) { return NULL; } else { char *reason_str = get_ordering_reason(order_data, order_node_arr_index, data->config_check_data->order_conf); if (!reason_str) { return alloc_internal_error("Failed to compute reason C-001"); } struct check_result *to_ret = make_check_result('C', C_ID_TE_ORDER, "%s", reason_str); free(reason_str); return to_ret; } } } return alloc_internal_error("Could not find ordering info for line"); } return NULL; } struct check_result *check_unordered_perms(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { const struct string_list *prev = NULL, *cur = NULL; const char *flavor; if (node->flavor == NODE_AV_RULE) { cur = node->data.av_data->perms; flavor = "av rule"; } else if (node->flavor == NODE_XAV_RULE) { cur = node->data.xav_data->perms; flavor = "xav rule"; } else if (node->flavor == NODE_DECL) { // ignore non-class declarations if (node->data.d_data->flavor != DECL_CLASS) { return NULL; } cur = node->data.d_data->attrs; flavor = "class declaration"; } else { return alloc_internal_error("Invalid node type for `check_unordered_perms`"); } while (cur) { if (prev && strcmp(prev->string, "~") != 0 && strcmp(cur->string, "-") != 0) { const int compare = strcmp(prev->string, cur->string); if (compare > 0) { return make_check_result('C', C_ID_UNORDERED_PERM, "Permissions in %s not ordered (%s before %s)", flavor, prev->string, cur->string); } else if (compare == 0) { return make_check_result('C', C_ID_UNORDERED_PERM, "Permissions in %s repeated (%s)", flavor, cur->string); } } prev = cur; cur = cur->next; } return NULL; } struct check_result *check_no_self(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->flavor != NODE_AV_RULE && node->flavor != NODE_XAV_RULE) { return alloc_internal_error("Bad node type given to check C-007"); } const struct av_rule_data *av_data = node->data.av_data; if (av_data->sources->next || av_data->targets->next || 0 == strcmp(av_data->targets->string, "self") || 0 != strcmp(av_data->sources->string, av_data->targets->string)) { return NULL; } if (av_data->sources->string[0] == '$') { // On variables, we skip unless they are "_t" suffixed if (!ends_with(av_data->sources->string, strlen(av_data->sources->string), "_t", 2)) { return NULL; } } else if (!look_up_in_decl_map(av_data->sources->string, DECL_TYPE)) { // skip attributes return NULL; } return make_check_result('C', C_ID_SELF, "Recommend use of self keyword instead of redundant type"); } struct check_result *check_foreign_cond_id(const struct check_data *data, const struct policy_node *node) { if (node->flavor != NODE_BOOLEAN_POLICY && node->flavor != NODE_TUNABLE_POLICY) { return alloc_internal_error("Invalid node type for `check_foreign_cond_id`"); } const struct string_list *identifiers = node->data.cd_data->identifiers; for (const struct string_list *ids = identifiers; ids; ids = ids->next) { const char *id = ids->string; const char *mod_name = look_up_in_decl_map(id, DECL_BOOL); /* Ignore non-existent identifiers, covered by W-012 */ if (mod_name == NULL) { continue; } if (0 == strcmp(mod_name, data->mod_name)) { continue; } if (0 == strcmp(mod_name, "__global__")) { continue; } return make_check_result('C', C_ID_FOREIGN_CONDID, "Identifier %s in expression for conditional block not found in own module, but in module %s (candidate for global declaration or interface)", id, mod_name); } return NULL; } struct check_result *check_require_block(const struct check_data *data, const struct policy_node *node) { if (data->flavor != FILE_TE_FILE) { return NULL; } struct policy_node *cur = node->first_child; while (cur) { if (cur->flavor != NODE_DECL) { cur = cur->next; continue; } if (cur->data.d_data->flavor != DECL_CLASS && cur->data.d_data->flavor != DECL_PERM) { return make_check_result('S', S_ID_REQUIRE, "Require block used in te file (use an interface call instead)"); } cur = cur->next; } // Require contained only object classes and permissions return NULL; } struct check_result *check_useless_semicolon(__attribute__((unused)) const struct check_data *data, __attribute__((unused)) const struct policy_node *node) { return make_check_result('S', S_ID_SEMICOLON, "Unnecessary semicolon"); } struct check_result *check_bare_module_statement(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->data.h_data->flavor == HEADER_BARE) { return make_check_result('S', S_ID_BARE_MODULE, "Bare module statement (use `policy_module()` instead)"); } return NULL; } // check if all classes are netlink socket classes static bool all_netlink_socket_classes(const struct string_list *classes) { for (; classes; classes = classes->next) { if (0 != strncmp(classes->string, "netlink_", strlen("netlink_"))) { return false; } } return true; } // check if all classes are socket classes static bool all_socket_classes(const struct string_list *classes) { for (; classes; classes = classes->next) { if (!ends_with(classes->string, strlen(classes->string), "_socket", strlen("_socket"))) { return false; } } return true; } // check if '$STR' ends with '$SUFFIX_perms' static bool ends_with_suffix_perms(const char *str, size_t str_len, const char *suffix, size_t suffix_len) { if (str_len < (suffix_len + strlen("_perms"))) { return 0; } // no need to check last 6 characters are actual '_perms' // we call this only on strings we have checked to have this suffix return (0 == strncmp(str + str_len - (suffix_len + strlen("_perms")), suffix, suffix_len)); } static bool ends_with_all_suffix_perms(const char *str, size_t str_len, const struct string_list *classes) { for (; classes; classes = classes->next) { if (!ends_with_suffix_perms(str, str_len, classes->string, strlen(classes->string))) { // check class alias as fallback static const char *const class_aliases[][2] = { { "chr_file", "term" }, { "process", "signal" }, }; const char *class_alias = NULL; for (size_t i = 0; i < (sizeof class_aliases / sizeof *class_aliases); ++i) { if (0 == strcmp(classes->string, class_aliases[i][0])) { class_alias = class_aliases[i][1]; break; } } if (!class_alias || !ends_with_suffix_perms(str, str_len, class_alias, strlen(class_alias))) { return false; } } } return true; } struct check_result *check_perm_macro_class_mismatch(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { static const char *const file_suffix_classes[] = { "lnk_file", "chr_file", "blk_file", "sock_file", "fifo_file", }; const struct string_list *classes = node->data.av_data->object_classes; // ignore class set av rules if (ends_with(classes->string, strlen(classes->string), "_class_set", strlen("_class_set"))) { return NULL; } const bool is_file_class = str_in_sl("file", classes); const bool is_netlink_socket_class = all_netlink_socket_classes(classes); const bool is_socket_class = all_socket_classes(classes); for (const struct string_list *perms = node->data.av_data->perms; perms; perms = perms->next) { const size_t perm_len = strlen(perms->string); // ignore permissions without '_perms' suffix; they are probably not macros if (!ends_with(perms->string, perm_len, "_perms", strlen("_perms"))) { continue; } // ignore permissions matching 'something[_something]_$CLASSNAME_perms' // and 'something[_something]_$CLASSNAMEALIAS_perms' if (ends_with_all_suffix_perms(perms->string, perm_len, classes)) { // report usage of macros matching different class with actual class as suffix // e.g. report 'something_fifo_file_perms' for class 'file' if (is_file_class) { for (size_t i = 0; i < (sizeof file_suffix_classes / sizeof *file_suffix_classes); ++i) { if (ends_with_suffix_perms(perms->string, perm_len, file_suffix_classes[i], strlen(file_suffix_classes[i]))) { goto report; } } } continue; } // ignore permissions 'something[_something]_netlink_socket_perms' for netlink classes if (is_netlink_socket_class && ends_with(perms->string, perm_len, "_socket_perms", strlen("_socket_perms"))) { continue; } // ignore permissions 'something[_something]_socket_perms' for (non-netlink) socket classes if (is_socket_class && !is_netlink_socket_class && ends_with(perms->string, perm_len, "_socket_perms", strlen("_socket_perms")) && !ends_with(perms->string, perm_len, "netlink_socket_perms", strlen("netlink_socket_perms"))) { continue; } report: return make_check_result('S', S_ID_PERM_SUFFIX, "Permission macro %s does not match class %s", perms->string, classes->next ? "(multi class av rule)" : classes->string); } return NULL; } struct check_result *check_perm_macro_available(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { // ignore non allow rules if (node->data.av_data->flavor != AV_RULE_ALLOW) { return NULL; } // ignore multi class av rules const struct string_list *class = node->data.av_data->object_classes; if (class->next || ends_with(class->string, strlen(class->string), "_class_set", strlen("_class_set"))) { return NULL; } char *check_str = permmacro_check(node->data.av_data->object_classes->string, node->data.av_data->perms); if (!check_str) { return NULL; } struct check_result *res = make_check_result('S', S_ID_PERMMACRO, "%s", check_str); free(check_str); return res; } // Helper for check_no_explicit_declaration. Returns 1 is there is a require block // for name earlier in the file, and 0 otherwise static int has_require(const struct policy_node *node, const char *name, enum decl_flavor flavor) { const struct policy_node *cur = node; while (cur) { if (cur->flavor == NODE_REQUIRE || cur->flavor == NODE_GEN_REQ) { cur = cur->first_child; while (1) { if (cur->flavor == NODE_DECL && cur->data.d_data->flavor == flavor) { if (0 == strcmp(name, cur->data.d_data->name)) { return 1; } struct string_list *other_types = cur->data.d_data->attrs; // In requires these // are types, not // attributes while (other_types) { if (0 == strcmp(name, other_types->string)) { return 1; } other_types = other_types->next; } } if (cur->next) { cur = cur->next; } else { break; } } // Not found in this require block, keep going cur = cur->parent; if (!cur) { break; } } if (cur->prev) { cur = cur->prev; } else { cur = cur->parent; } } return 0; } struct check_result *check_no_explicit_declaration(const struct check_data *data, const struct policy_node *node) { if (data->flavor != FILE_TE_FILE) { return NULL; } struct name_list *names = get_names_in_node(node); const struct name_list *name = names; /* In declarations skip the first name, which is the new declared type */ if (node->flavor == NODE_DECL) { name = name->next; } for (; name; name = name->next) { const struct name_data *ndata = name->data; const char *mod_name; enum decl_flavor flavor; if (name_is_type(ndata) && (mod_name = look_up_in_decl_map(ndata->name, DECL_TYPE))) { flavor = DECL_TYPE; } else if (name_is_typeattr(ndata) && (mod_name = look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE))) { flavor = DECL_ATTRIBUTE; } else if (name_is_roleattr(ndata) && (mod_name = look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE_ROLE))) { flavor = DECL_ATTRIBUTE_ROLE; } else if (name_is_class(ndata) && look_up_in_decl_map(ndata->name, DECL_CLASS)) { // Ignore kernel classes if (!userspace_class_support || !is_userspace_class(ndata->name, ndata->traits)) { continue; } flavor = DECL_CLASS; mod_name = NULL; // Do not check for roles: in refpolicy they are defined in the kernel module // and used in role modules (like unprivuser) } else { //Not a known name continue; } if (!mod_name || 0 != strcmp(data->mod_name, mod_name)) { // It may be required if (!has_require(node, ndata->name, flavor)) { // We didn't find a require block with this name struct check_result *to_ret; if (flavor == DECL_CLASS) { to_ret = make_check_result('W', W_ID_NO_EXPLICIT_DECL, "No explicit declaration for userspace class %s. You should access it via interface call or use a require block.", ndata->name); } else { to_ret = make_check_result('W', W_ID_NO_EXPLICIT_DECL, "No explicit declaration for %s from module %s. You should access it via interface call or use a require block.", ndata->name, mod_name); } free_name_list(names); return to_ret; } // Otherwise, keep checking other names in this node } } free_name_list(names); return NULL; } struct check_result *check_module_if_call_in_optional(const struct check_data *data, const struct policy_node *node) { const struct if_call_data *if_data = node->data.ic_data; const char *if_mod_name = look_up_in_ifs_map(if_data->name); if (!if_mod_name) { // Not defined as an interface. Probably a macro return NULL; } if (0 == strcmp(if_mod_name, data->mod_name)) { // No issue calling interfaces in your own module return NULL; } const char *mod_type = look_up_in_mods_map(if_mod_name); if (!mod_type) { // If mod_type is NULL, we have no info on this module. We *should* have info // on all modules of type module, but in some cases may be missing ones that are // off or base. Off and base pass the check. return NULL; } if (0 == strcmp(mod_type, "base")) { // No issue calling interfaces in base module return NULL; } const struct policy_node *tmp = node; while (tmp->parent) { tmp = tmp->parent; if (tmp->flavor == NODE_OPTIONAL_POLICY) { return NULL; } } return make_check_result('W', W_ID_IF_CALL_OPTIONAL, "Call to interface %s defined in module %s should be in optional_policy block", if_data->name, if_mod_name); } struct check_result *check_empty_if_call_arg(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (!node->data.ic_data->args) { return make_check_result('W', W_ID_EMPTY_IF_CALL_ARG, "Call to interface %s with empty argument", node->data.ic_data->name); } return NULL; } struct check_result *check_space_if_call_arg(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { const struct string_list *prev = NULL, *args = node->data.ic_data->args; unsigned short i = 1; while (args) { if (args->has_incorrect_space) { // do not issue on mls ranges if (prev && (args->string[0] != '-' || look_up_in_decl_map(prev->string, DECL_TYPE) || look_up_in_decl_map(prev->string, DECL_ATTRIBUTE) || look_up_in_decl_map(prev->string, DECL_ROLE) || look_up_in_decl_map(prev->string, DECL_ATTRIBUTE_ROLE) || look_up_in_decl_map(prev->string, DECL_USER))) { return make_check_result('W', W_ID_SPACE_IF_CALL_ARG, "Argument no. %u '%s ...' of call to interface %s contains unquoted space", i - 1, // need to subtract one, cause it is the next string who has the flag set prev->string, node->data.ic_data->name); } } else { i++; } prev = args; args = args->next; } return NULL; } struct check_result *check_risky_allow_perm(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { // ignore non-allow rules if (node->data.av_data->flavor != AV_RULE_ALLOW) { return NULL; } const struct string_list *perms = node->data.av_data->perms; while (perms) { if (0 == strcmp(perms->string, "*") || 0 == strcmp(perms->string, "~")) { return make_check_result('W', W_ID_RISKY_ALLOW_PERM, "Allow rule with complement or wildcard permission"); } perms = perms->next; } return NULL; } struct check_result *check_module_file_name_mismatch(const struct check_data *data, const struct policy_node *node) { const char *mod_name = node->data.h_data->module_name; size_t mod_name_len = strlen(mod_name); const char *file_name = data->filename; size_t file_name_len = strlen(file_name); const char *file_name_ext = strrchr(file_name, '.'); if (file_name_ext) { file_name_len -= strlen(file_name_ext); } if (mod_name_len != file_name_len || strncmp(mod_name, file_name, file_name_len)) { return make_check_result('W', W_ID_MOD_NAME_FILE, "Module name %s does not match file name %s", mod_name, file_name); } return NULL; } static bool starts_with_module_prefix(const char *name) { for (const char *prefix = strchr(name, '_'); prefix; prefix = strchr(prefix + 1, '_')) { char *search_mod = xstrndup(name, (size_t)(prefix - name)); if (look_up_in_mods_map(search_mod)) { free(search_mod); return true; } for (size_t i = 0; i < (sizeof RefPol_module_abbreviations / sizeof *RefPol_module_abbreviations); ++i) { if (0 == strcmp(search_mod, RefPol_module_abbreviations[i][0]) && look_up_in_mods_map(RefPol_module_abbreviations[i][1])) { free(search_mod); return true; } } free(search_mod); } return false; } struct check_result *check_unknown_interface_call(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { const char *if_name = node->data.ic_data->name; // ignore known macros starting with module name for (size_t i = 0; i < (sizeof RefPol_macros_with_module_prefix / sizeof *RefPol_macros_with_module_prefix); ++i) { if (0 == strcmp(if_name, RefPol_macros_with_module_prefix[i])) { return NULL; } } // ignore calls which does not start with a module name: they are probably macros if (!starts_with_module_prefix(if_name)) { return NULL; } // ignore known interfaces if (look_up_in_ifs_map(if_name)) { return NULL; } return make_check_result('W', W_ID_UNKNOWN_CALL, "Call to %s can not be referenced to any interface", if_name); } /* Check whether the name is a simple parameter, like $1. Combined ones like $1_t are not considered. */ static bool is_bare_parameter(const char *name) { if (name[0] != '$' || name[1] == '\0') { return false; } for (const char *c = name + 1; *c; c++) { if (!isdigit((unsigned char)*c)) { return false; } } return true; } static bool temp_has_bool_decl(const char *temp_name, const char *bool_name) { if (temp_name == NULL) { return false; } for (const struct decl_list *dl = look_up_decl_in_template_map(temp_name); dl; dl = dl->next) { const struct declaration_data *decl = dl->decl; if (decl->flavor != DECL_BOOL) { continue; } if (0 == strcmp(decl->name, bool_name)) { return true; } } return false; } struct check_result *check_unknown_cond_id(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->flavor != NODE_BOOLEAN_POLICY && node->flavor != NODE_TUNABLE_POLICY) { return alloc_internal_error("Invalid node type for `check_foreign_cond_id`"); } const struct string_list *identifiers = node->data.cd_data->identifiers; for (const struct string_list *ids = identifiers; ids; ids = ids->next) { const char *id = ids->string; /* Ignore interface parameters */ if (is_bare_parameter(id)) { continue; } if (look_up_in_decl_map(id, DECL_BOOL) != NULL) { continue; } if (temp_has_bool_decl(get_name_if_in_template(node), id)) { continue; } return make_check_result('W', W_ID_UNKNOWN_COND_ID, "Expression for conditional block uses unknown identifier %s", id); } return NULL; } struct check_result *check_audit_access_perm(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { if (node->data.av_data->flavor == AV_RULE_DONTAUDIT || node->data.av_data->flavor == AV_RULE_NEVERALLOW) { return NULL; } for (const struct string_list *perms = node->data.av_data->perms; perms; perms = perms->next) { if (0 == strcmp(perms->string, "audit_access")) { return make_check_result('W', W_ID_AUDIT_ACCESS, "Allow rule with audit_access permission"); } } return NULL; } struct check_result *check_declaration_interface_nameclash(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { const char *decl_name = node->data.d_data->name; if (look_up_in_ifs_map(decl_name)) { return make_check_result('E', E_ID_DECL_IF_CLASH, "Declaration with name %s clashes with same named interface", decl_name); } return NULL; } bool check_unknown_permission_condition(void) { // ignore if no permission or permission macro have been parsed if (permmacros_map_count() == 0) { printf("%sNote%s: Check E-007 is not performed because no permission macro has been parsed.\n", color_note(), color_reset()); return false; } if (decl_map_count(DECL_PERM) == 0) { printf("%sNote%s: Check E-007 is not performed because no permission has been parsed.\n", color_note(), color_reset()); return false; } return true; } struct check_result *check_unknown_permission(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { for (const struct string_list *cur = node->data.av_data->perms; cur; cur = cur->next) { if (0 == strcmp(cur->string, "*") || 0 == strcmp(cur->string, "~")) { continue; } // ignore generated all_ permission macros if (0 == strncmp(cur->string, "all_", strlen("all_")) && ends_with(cur->string, strlen(cur->string), "_perms", strlen("_perms"))) { continue; } if (look_up_in_decl_map(cur->string, DECL_PERM)) { // TODO: check if class supports this permission continue; } if (look_up_in_permmacros_map(cur->string)) { continue; } return make_check_result('E', E_ID_UNKNOWN_PERM, "Unknown permission %s used", cur->string); } return NULL; } bool check_unknown_class_condition(void) { // ignore if no class has been parsed if (decl_map_count(DECL_CLASS) == 0) { printf("%sNote%s: Check E-008 is not performed because no class has been parsed.\n", color_note(), color_reset()); return false; } return true; } struct check_result *check_unknown_class(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { const struct string_list *object_classes; switch (node->flavor) { case NODE_AV_RULE: object_classes = node->data.av_data->object_classes; break; case NODE_RT_RULE: object_classes = node->data.rt_data->object_classes; break; case NODE_TT_RULE: object_classes = node->data.tt_data->object_classes; break; default: return alloc_internal_error("Invalid node type for `check_unknown_class`"); } for (const struct string_list *cur = object_classes; cur; cur = cur->next) { // ignore interface parameters if (cur->string[0] == '$' && isdigit((unsigned char)cur->string[1])) { continue; } // ignore class sets if (ends_with(cur->string, strlen(cur->string), "_class_set", strlen("_class_set"))) { continue; } if (look_up_in_decl_map(cur->string, DECL_CLASS)) { continue; } return make_check_result('E', E_ID_UNKNOWN_CLASS, "Unknown class %s used", cur->string); } return NULL; } struct check_result *check_empty_block(__attribute__((unused)) const struct check_data *data, const struct policy_node *node) { for (const struct policy_node *cur = node->first_child; cur; cur = cur->next) { if (cur->flavor == NODE_START_BLOCK || cur->flavor == NODE_COMMENT || cur->flavor == NODE_SEMICOLON) { continue; } // found a statement return NULL; } return make_check_result('E', E_ID_EMPTY_BLOCK, "Empty block found"); } static const struct policy_node *is_ifelse_argument(const struct policy_node *cur) { for(; cur && cur->parent; cur = cur->parent) { if (cur->flavor == NODE_M4_ARG && cur->parent->flavor == NODE_IFELSE) { return cur; } } return NULL; } struct check_result *check_stray_word(const struct check_data *data, const struct policy_node *node) { const char *macro_name = node->data.str; if (str_in_sl(macro_name, data->config_check_data->custom_te_simple_macros)) { return NULL; } // ignore comparison arguments to ifelse // (do not ignore last node of block, which is never a comparison argument) const struct policy_node *ifelse_argument = is_ifelse_argument(node); if (ifelse_argument && ifelse_argument->next) { int position = 0; for (const struct policy_node *cur = ifelse_argument; cur->prev; cur = cur->prev) { position++; } position = position % 3; if ((position == 1 || position == 2)) { return NULL; } } return make_check_result('E', E_ID_STRAY_WORD, "Found stray word %s. If it is a simple m4 macro please add an selint-disable comment or ignore in the SELint configuration file.", macro_name); } selint-1.5.1/src/te_checks.h000066400000000000000000000351261475050262500157210ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TE_CHECKS_H #define TE_CHECKS_H #include "check_hooks.h" /********************************************* * Check for access vector rules with excluded source or target. * Called on NODE_AV_RULE and NODE_IF_CALL nodes. * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue X-002 *********************************************/ struct check_result *check_excluding_av_rule(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for violations of te file ordering conventions. * The refpolicy conventions (which is all that can be checked as of now are * described at: https://github.com/SELinuxProject/refpolicy/wiki/StyleGuide * Called on all nodes except fc file nodes, error nodes, NODE_IF_FILE and * NODE_FC_FILE * On NODE_TE_FILE nodes, it generates the ordering information for that file * On other nodes, it checks the previously generated ordering information to * determine whether to return an error. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue C-001 *********************************************/ struct check_result *check_te_order(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for unordered permissions in av rules and class declarations. * Called on NODE_AV_RULE, NODE_XAV_RULE and NODE_DECL nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue C-005 *********************************************/ struct check_result *check_unordered_perms(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for av rules which could use the self keyword but do not. * Note that "av_rule attr_name self:..." and "av_rule attr_name attr_name:..." * Are not identical in behavior, so this should only detect types, not * attributes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue C-007 *********************************************/ struct check_result *check_no_self(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for identifiers in conditional expressions not declared in own module. * Called on NODE_BOOLEAN_POLICY and NODE_TUNABLE_POLICY nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue C-008 *********************************************/ struct check_result *check_foreign_cond_id(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for the presence of require blocks in TE files. * Interface calls are to be preferred. * Called on NODE_REQUIRE and NODE_GEN_REQ nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue S-001 *********************************************/ struct check_result *check_require_block(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for useless semicolons after interface calls * Called on IF_CALL nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue S-003 *********************************************/ struct check_result *check_useless_semicolon(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for bare module statements * Called on NODE_HEADER nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue S-006 *********************************************/ struct check_result *check_bare_module_statement(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for name mismatch between permission macro and class name. * Called on NODE_AV_RULE nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue S-009 *********************************************/ struct check_result *check_perm_macro_class_mismatch(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for used permissions available by a permission macro * Called on NODE_AV_RULE nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue S-010 *********************************************/ struct check_result *check_perm_macro_available(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for references to types in te files without an explicit declaration. * We don't check types in .if or .fc files because those are similar issues * handled by W-002 and E-005 respectively. * This situation typically results in a compilation error, but in the event * that an earlier interface call required the type it would not. * Called on allow rule and interface call nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue W-001 *********************************************/ struct check_result *check_no_explicit_declaration(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for situations where interface or template calls into modules are not * in optional policy blocks * Called on NODE_IF_CALL nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue W-005 *********************************************/ struct check_result *check_module_if_call_in_optional(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for interface calls with empty argument * Called on NODE_IF_CALL nodes * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue W-006 *********************************************/ struct check_result *check_empty_if_call_arg(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for unquoted space in argument of interface calls * Called on NODE_IF_CALL nodes * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue W-007 *********************************************/ struct check_result *check_space_if_call_arg(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for allow rule with complement or wildcard permission * Called on NODE_AV_RULE nodes * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue W-008 *********************************************/ struct check_result *check_risky_allow_perm(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for mismatch of module and file names. * Called on NODE_HEADER nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue W-009 *********************************************/ struct check_result *check_module_file_name_mismatch(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for call of unknown interface. * Called on NODE_IF_CALL nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue W-010 *********************************************/ struct check_result *check_unknown_interface_call(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for unknown identifiers in conditional expressions. * Called on NODE_BOOLEAN_POLICY and NODE_TUNABLE_POLICY nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue W-012 *********************************************/ struct check_result *check_unknown_cond_id(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for allow rule with audit_access permission * Called on NODE_AV_RULE nodes * data - metadata about the file * node - the node to check * returns NULL if passed or check_result for issue W-013 *********************************************/ struct check_result *check_audit_access_perm(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for clash of declaration and interface names. * This will cause macro expansion to enter an endless loop * and consume all available memory. * Called on NODE_DECL nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue E-006 *********************************************/ struct check_result *check_declaration_interface_nameclash(const struct check_data *data, const struct policy_node *node); /********************************************* * Verify whether the next check can be enabled. *********************************************/ bool check_unknown_permission_condition(void); /********************************************* * Check for usage of unknown permission or permission macro. * Called on NODE_AV_RULE nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue E-007 *********************************************/ struct check_result *check_unknown_permission(const struct check_data *data, const struct policy_node *node); /********************************************* * Verify whether the next check can be enabled. *********************************************/ bool check_unknown_class_condition(void); /********************************************* * Check for usage of unknown class. * Called on NODE_AV_RULE, NODE_RT_RULE and NODE_TT_RULE nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue E-008 *********************************************/ struct check_result *check_unknown_class(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for empty optional and require macro blocks. * Called on NODE_OPTIONAL_POLICY, NODE_GEN_REQ and NODE_REQUIRE nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue E-009 *********************************************/ struct check_result *check_empty_block(const struct check_data *data, const struct policy_node *node); /********************************************* * Check for stray words. * Called on NODE_M4_SIMPLE_MACRO nodes. * data - metadata about the file currently being scanned * node - the node to check * returns NULL if passed or check_result for issue E-010 *********************************************/ struct check_result *check_stray_word(const struct check_data *data, const struct policy_node *node); #endif selint-1.5.1/src/template.c000066400000000000000000000100731475050262500155710ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "template.h" #include "maps.h" #include "xalloc.h" char *replace_m4(const char *orig, const struct string_list *args) { size_t len_to_malloc = strlen(orig) + 1; const struct string_list *cur = args; while (cur) { len_to_malloc += strlen(cur->string); cur = cur->next; } // len_to_malloc is now overestimated, because the length of the original // arguments wasn't subtracted and not all args are necessarily substituted char *ret = xmalloc(len_to_malloc); *ret = '\0'; // If the string is only a substitution that there is no argument for, we need to be terminated const char *orig_pos = orig; char *ret_pos = ret; while (*orig_pos) { int arg_num; int after_num_pos; const char *dollar_pos = strchr(orig_pos, '$'); if (!dollar_pos) { strcpy(ret_pos, orig_pos); break; } strncpy(ret_pos, orig_pos, (size_t)(dollar_pos - orig_pos)); ret_pos += dollar_pos - orig_pos; orig_pos = dollar_pos; int ret_count = sscanf(orig_pos, "$%d%n", &arg_num, &after_num_pos); if (ret_count != 1) { // %n doesn't count for return of sscanf free(ret); return NULL; } orig_pos += after_num_pos; cur = args; while (cur && arg_num > 1) { cur = cur->next; arg_num--; } if (cur) { strcpy(ret_pos, cur->string); ret_pos += strlen(cur->string); } // Otherwise, we are inserting the empty string } return ret; } struct string_list *replace_m4_list(const struct string_list *replace_with, const struct string_list *replace_from) { struct string_list *ret = xcalloc(1, sizeof(struct string_list)); struct string_list *cur = ret; cur->string = replace_m4(replace_from->string, replace_with); cur->next = NULL; replace_from = replace_from->next; while (replace_from) { cur->next = xcalloc(1, sizeof(struct string_list)); cur = cur->next; cur->string = replace_m4(replace_from->string, replace_with); cur->next = NULL; replace_from = replace_from->next; } return ret; } enum selint_error add_template_declarations(const char *template_name, const struct string_list *args, struct string_list *parent_temp_names, const char *mod_name) { struct string_list *cur = parent_temp_names; while (cur) { if (strcmp(cur->string, template_name) == 0) { // Loop free_string_list(parent_temp_names); return SELINT_IF_CALL_LOOP; } cur = cur->next; } cur = xcalloc(1, sizeof(struct string_list)); cur->string = xstrdup(template_name); cur->next = parent_temp_names; const struct if_call_list *calls = look_up_call_in_template_map(template_name); while (calls) { struct string_list *new_args = replace_m4_list(args, calls->call->args); enum selint_error res = add_template_declarations(calls->call->name, new_args, cur, mod_name); free_string_list(new_args); if (res != SELINT_SUCCESS) { return res; } calls = calls->next; } const struct decl_list *decls = look_up_decl_in_template_map(template_name); while (decls) { char *new_decl = replace_m4(decls->decl->name, args); if (!new_decl) { free(cur->string); free(cur); return SELINT_M4_SUB_FAILURE; } insert_into_decl_map(new_decl, mod_name, decls->decl->flavor); free(new_decl); decls = decls->next; } free(cur->string); free(cur); return SELINT_SUCCESS; } selint-1.5.1/src/template.h000066400000000000000000000026501475050262500156000ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TEMPLATE_H #define TEMPLATE_H // Functions for dealing with declarations in templates #include "selint_error.h" #include "tree.h" /* Replace bash style arguments with a string */ char *replace_m4(const char *orig, const struct string_list *args); /* Loop over replace_from string_list and call replace_m4 on each string in it, using the * strings in replace_with as the arguments. */ struct string_list *replace_m4_list(const struct string_list *replace_with, const struct string_list *replace_from); enum selint_error add_template_declarations(const char *template_name, const struct string_list *args, struct string_list *parent_temp_names, const char *mod_name); #endif selint-1.5.1/src/tree.c000066400000000000000000000325541475050262500147250ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "tree.h" #include "maps.h" #include "selint_error.h" #include "xalloc.h" enum selint_error insert_policy_node_child(struct policy_node *parent, enum node_flavor flavor, union node_data data, unsigned int lineno) { if (parent == NULL) { return SELINT_BAD_ARG; } struct policy_node *to_insert = xmalloc(sizeof(struct policy_node)); to_insert->parent = parent; to_insert->next = NULL; to_insert->first_child = NULL; to_insert->flavor = flavor; to_insert->data = data; to_insert->exceptions = NULL; to_insert->lineno = lineno; if (parent->first_child == NULL) { parent->first_child = to_insert; to_insert->prev = NULL; } else { struct policy_node *cur = parent->first_child; while (cur->next != NULL) { cur = cur->next; } cur->next = to_insert; to_insert->prev = cur; } return SELINT_SUCCESS; } enum selint_error insert_policy_node_next(struct policy_node *prev, enum node_flavor flavor, union node_data data, unsigned int lineno) { if (prev == NULL) { return SELINT_BAD_ARG; } struct policy_node *to_insert = xmalloc(sizeof(struct policy_node)); prev->next = to_insert; to_insert->parent = prev->parent; to_insert->next = NULL; to_insert->first_child = NULL; to_insert->flavor = flavor; to_insert->data = data; to_insert->prev = prev; to_insert->exceptions = NULL; to_insert->lineno = lineno; return SELINT_SUCCESS; } int is_template_call(const struct policy_node *node) { if (node == NULL || node->data.ic_data == NULL) { return 0; } if (node->flavor != NODE_IF_CALL) { return 0; } const char *call_name = node->data.ic_data->name; if (look_up_in_template_map(call_name)) { return 1; } return 0; } const char *get_name_if_in_template(const struct policy_node *cur) { while (cur->parent) { cur = cur->parent; if (cur->flavor == NODE_TEMP_DEF) { return cur->data.str; } } return NULL; } const char *decl_flavor_to_string(enum decl_flavor flavor) { switch (flavor) { case DECL_TYPE: return "type"; case DECL_ATTRIBUTE: return "attribute"; case DECL_ATTRIBUTE_ROLE: return "attribute role"; case DECL_ROLE: return "role"; case DECL_USER: return "user"; case DECL_CLASS: return "class"; case DECL_PERM: return "permission"; case DECL_BOOL: return "boolean"; default: return "unknown"; } } struct name_list *get_names_in_node(const struct policy_node *node) { struct name_list *ret = NULL; struct name_list *cur = NULL; const struct av_rule_data *av_data; const struct type_transition_data *tt_data; const struct role_transition_data *rt_data; const struct declaration_data *d_data; const struct if_call_data *ifc_data; const struct role_allow_data *ra_data; const struct role_types_data *rtyp_data; const struct attribute_data *at_data; switch (node->flavor) { case NODE_AV_RULE: case NODE_XAV_RULE: // Since the common elements are ordered identically, we can just look // at the common subset for the XAV rule av_data = node->data.av_data; ret = name_list_from_sl(av_data->sources, NAME_TYPE_OR_ATTRIBUTE); ret = concat_name_lists(ret, name_list_from_sl(av_data->targets, NAME_TYPE_OR_ATTRIBUTE)); ret = concat_name_lists(ret, name_list_from_sl_with_traits(av_data->object_classes, NAME_CLASS, av_data->perms)); break; case NODE_TT_RULE: tt_data = node->data.tt_data; ret = name_list_from_sl(tt_data->sources, NAME_TYPE_OR_ATTRIBUTE); ret = concat_name_lists(ret, name_list_from_sl(tt_data->targets, NAME_TYPE_OR_ATTRIBUTE)); ret = concat_name_lists(ret, name_list_create(tt_data->default_type, NAME_TYPE)); ret = concat_name_lists(ret, name_list_from_sl(tt_data->object_classes, NAME_CLASS)); break; case NODE_RT_RULE: rt_data = node->data.rt_data; ret = name_list_from_sl(rt_data->sources, NAME_ROLE_OR_ATTRIBUTE); ret = concat_name_lists(ret, name_list_from_sl(rt_data->targets, NAME_TYPE_OR_ATTRIBUTE)); ret = concat_name_lists(ret, name_list_create(rt_data->default_role, NAME_ROLE)); ret = concat_name_lists(ret, name_list_from_sl(rt_data->object_classes, NAME_CLASS)); break; case NODE_DECL: d_data = node->data.d_data; ret = name_list_from_decl(d_data); break; case NODE_IF_CALL: ifc_data = node->data.ic_data; ret = name_list_from_sl(ifc_data->args, NAME_UNKNOWN); break; case NODE_ROLE_ALLOW: ra_data = node->data.ra_data; ret = name_list_from_sl(ra_data->from, NAME_ROLE_OR_ATTRIBUTE); ret = concat_name_lists(ret, name_list_from_sl(ra_data->to, NAME_ROLE_OR_ATTRIBUTE)); break; case NODE_ROLE_TYPES: rtyp_data = node->data.rtyp_data; ret = name_list_create(rtyp_data->role, NAME_ROLE_OR_ATTRIBUTE); ret->next = name_list_from_sl(rtyp_data->types, NAME_TYPE_OR_ATTRIBUTE); break; case NODE_TYPE_ATTRIBUTE: at_data = node->data.at_data; ret = name_list_create(at_data->type, NAME_TYPE); ret->next = name_list_from_sl(at_data->attrs, NAME_TYPEATTRIBUTE); break; case NODE_ROLE_ATTRIBUTE: at_data = node->data.at_data; ret = name_list_create(at_data->type, NAME_ROLE); ret->next = name_list_from_sl(at_data->attrs, NAME_ROLEATTRIBUTE); break; case NODE_ALIAS: case NODE_TYPE_ALIAS: case NODE_PERMISSIVE: ret = name_list_create(node->data.str, NAME_TYPE); break; /* NODE_TE_FILE, NODE_IF_FILE, NODE_FC_FILE, NODE_SPT_FILE, NODE_AV_FILE, NODE_COND_FILE, NODE_HEADER, NODE_M4_CALL, NODE_M4_SIMPLE_MACRO, NODE_DEFINE, NODE_OPTIONAL_POLICY, NODE_OPTIONAL_ELSE, NODE_BOOLEAN_POLICY, NODE_TUNABLE_POLICY, NODE_IFDEF, NODE_IFELSE, NODE_M4_ARG, NODE_START_BLOCK, NODE_INTERFACE_DEF, NODE_TEMP_DEF, NODE_REQUIRE, NODE_GEN_REQ, NODE_FC_ENTRY, NODE_COMMENT, NODE_EMPTY, NODE_SEMICOLON, NODE_CLEANUP, NODE_ERROR */ default: break; } // Check if any of the types are exclusions cur = ret; while (cur) { char *name = cur->data->name; if (name[0] == '-') { // memmove is safe for overlapping strings // Length is strlen exactly because it doesn't copy the first // character, but does copy the null terminator memmove(name, name + 1, strlen(name)); } cur = cur->next; } return ret; } struct name_list *get_names_required(const struct policy_node *node) { struct name_list *ret = NULL; struct name_list *ret_cursor = NULL; struct policy_node *cur = node->first_child; while (cur) { if (ret_cursor) { ret_cursor->next = get_names_in_node(cur); } else { ret = ret_cursor = get_names_in_node(cur); } while (ret_cursor && ret_cursor->next) { ret_cursor = ret_cursor->next; } cur = cur->next; } return ret; } int is_in_require(const struct policy_node *cur) { while (cur->parent) { cur = cur->parent; if (cur->flavor == NODE_GEN_REQ || cur->flavor == NODE_REQUIRE) { return 1; } } return 0; } // Note: Not template define int is_in_if_define(const struct policy_node *cur) { while (cur->parent) { cur = cur->parent; if (cur->flavor == NODE_INTERFACE_DEF) { return 1; } } return 0; } struct policy_node *dfs_next(const struct policy_node *node) { if (node->first_child) { return node->first_child; } else if (node->next) { return node->next; } else { while (node->parent && !node->parent->next) { node = node->parent; } if (node->parent) { return node->parent->next; } else { return NULL; } } } static void free_single_policy_node_data(struct policy_node *to_free) { switch (to_free->flavor) { case NODE_HEADER: free_header_data(to_free->data.h_data); break; case NODE_AV_RULE: free_av_rule_data(to_free->data.av_data); break; case NODE_XAV_RULE: free_xav_rule_data(to_free->data.xav_data); break; case NODE_ROLE_ALLOW: free_ra_data(to_free->data.ra_data); break; case NODE_ROLE_TYPES: free_rtyp_data(to_free->data.rtyp_data); break; case NODE_TT_RULE: free_type_transition_data(to_free->data.tt_data); break; case NODE_RT_RULE: free_role_transition_data(to_free->data.rt_data); break; case NODE_IF_CALL: free_if_call_data(to_free->data.ic_data); break; case NODE_DECL: free_declaration_data(to_free->data.d_data); break; case NODE_FC_ENTRY: free_fc_entry(to_free->data.fc_data); break; case NODE_TYPE_ATTRIBUTE: case NODE_ROLE_ATTRIBUTE: free_attribute_data(to_free->data.at_data); break; case NODE_GEN_REQ: free_gen_require_data(to_free->data.gr_data); break; case NODE_BOOLEAN_POLICY: case NODE_TUNABLE_POLICY: free_cond_declaration_data(to_free->data.cd_data); break; default: if (to_free->data.str != NULL) { free(to_free->data.str); } break; } free(to_free->exceptions); } enum selint_error free_policy_node(struct policy_node *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } do { struct policy_node *next = to_free->next; free_single_policy_node_data(to_free); free_policy_node(to_free->first_child); free(to_free); to_free = next; } while (to_free); return SELINT_SUCCESS; } enum selint_error free_header_data(struct header_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free(to_free->module_name); free(to_free); return SELINT_SUCCESS; } enum selint_error free_av_rule_data(struct av_rule_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free_string_list(to_free->sources); free_string_list(to_free->targets); free_string_list(to_free->object_classes); free_string_list(to_free->perms); free(to_free); return SELINT_SUCCESS; } enum selint_error free_xav_rule_data(struct xav_rule_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free_string_list(to_free->sources); free_string_list(to_free->targets); free_string_list(to_free->object_classes); free(to_free->operation); free_string_list(to_free->perms); free(to_free); return SELINT_SUCCESS; } enum selint_error free_ra_data(struct role_allow_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free_string_list(to_free->from); free_string_list(to_free->to); free(to_free); return SELINT_SUCCESS; } enum selint_error free_rtyp_data(struct role_types_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free(to_free->role); free_string_list(to_free->types); free(to_free); return SELINT_SUCCESS; } enum selint_error free_type_transition_data(struct type_transition_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free_string_list(to_free->sources); free_string_list(to_free->targets); free_string_list(to_free->object_classes); free(to_free->default_type); free(to_free->name); free(to_free); return SELINT_SUCCESS; } enum selint_error free_role_transition_data(struct role_transition_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free_string_list(to_free->sources); free_string_list(to_free->targets); free_string_list(to_free->object_classes); free(to_free->default_role); free(to_free); return SELINT_SUCCESS; } enum selint_error free_if_call_data(struct if_call_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free(to_free->name); free_string_list(to_free->args); free(to_free); return SELINT_SUCCESS; } enum selint_error free_declaration_data(struct declaration_data *to_free) { if (to_free == NULL) { return SELINT_BAD_ARG; } free_string_list(to_free->attrs); free(to_free->name); free(to_free); return SELINT_SUCCESS; } enum selint_error free_decl_list(struct decl_list *to_free) { while (to_free) { free_declaration_data(to_free->decl); struct decl_list *tmp = to_free; to_free = to_free->next; free(tmp); } return SELINT_SUCCESS; } // The if call data structs in an if call list are pointers to data that is freed elsewhere enum selint_error free_if_call_list(struct if_call_list *to_free) { while (to_free) { struct if_call_list *tmp = to_free; to_free = to_free->next; free(tmp); } return SELINT_SUCCESS; } void free_fc_entry(struct fc_entry *to_free) { if (to_free->path) { free(to_free->path); } if (to_free->context) { free_sel_context(to_free->context); } free(to_free); } void free_sel_context(struct sel_context *to_free) { if (to_free->user) { free(to_free->user); } if (to_free->role) { free(to_free->role); } if (to_free->type) { free(to_free->type); } if (to_free->range) { free(to_free->range); } free(to_free); } void free_attribute_data(struct attribute_data *to_free) { if (to_free->type) { free(to_free->type); } if (to_free->attrs) { free_string_list(to_free->attrs); } free(to_free); } void free_gen_require_data(struct gen_require_data *to_free) { free(to_free); } void free_cond_declaration_data(struct cond_declaration_data *to_free) { free_string_list(to_free->identifiers); free(to_free); } selint-1.5.1/src/tree.h000066400000000000000000000164361475050262500147330ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TREE_H #define TREE_H #include "name_list.h" #include "selint_error.h" #include "string_list.h" enum node_flavor { NODE_TE_FILE, NODE_IF_FILE, NODE_FC_FILE, NODE_SPT_FILE, NODE_AV_FILE, NODE_COND_FILE, NODE_AV_RULE, NODE_XAV_RULE, NODE_TT_RULE, NODE_RT_RULE, NODE_HEADER, NODE_ROLE_ALLOW, NODE_ROLE_TYPES, NODE_DECL, NODE_ALIAS, NODE_TYPE_ALIAS, NODE_TYPE_ATTRIBUTE, NODE_ROLE_ATTRIBUTE, NODE_M4_CALL, NODE_M4_SIMPLE_MACRO, NODE_DEFINE, NODE_OPTIONAL_POLICY, NODE_OPTIONAL_ELSE, NODE_BOOLEAN_POLICY, NODE_TUNABLE_POLICY, NODE_IFDEF, NODE_IFELSE, NODE_M4_ARG, NODE_START_BLOCK, NODE_INTERFACE_DEF, NODE_TEMP_DEF, NODE_IF_CALL, NODE_REQUIRE, NODE_GEN_REQ, NODE_PERMISSIVE, NODE_FC_ENTRY, NODE_COMMENT, NODE_EMPTY, NODE_SEMICOLON, // A semicolon that is not needed to close the previous line NODE_CLEANUP, // Called after each file parsing is complete so that checks // that register on this node have a way to clean up state NODE_ERROR // When a parsing error occurs, save an error node in the tree // NODE_ERROR must be the last item in the node_flavor enum // as check_hooks.c assumes it when allocating an array of // length equal to the number of node types }; enum header_flavor { HEADER_BARE, HEADER_MACRO }; enum av_rule_flavor { AV_RULE_ALLOW, AV_RULE_AUDITALLOW, AV_RULE_DONTAUDIT, AV_RULE_NEVERALLOW }; enum decl_flavor { DECL_TYPE, DECL_ATTRIBUTE, DECL_ATTRIBUTE_ROLE, DECL_ROLE, DECL_USER, DECL_CLASS, DECL_PERM, DECL_BOOL }; enum attr_flavor { ATTR_TYPE, ATTR_ROLE }; enum tt_flavor { TT_TT, TT_TM, TT_TC, TT_RT }; struct header_data { enum header_flavor flavor; char *module_name; }; #define AV_RULE_MEMBERS \ enum av_rule_flavor flavor;\ struct string_list *sources;\ struct string_list *targets;\ struct string_list *object_classes;\ struct string_list *perms; struct av_rule_data { AV_RULE_MEMBERS; }; struct xav_rule_data { AV_RULE_MEMBERS; // All xav_rule specific members MUST go after AV_RULE ones char *operation; }; struct role_allow_data { struct string_list *from; struct string_list *to; }; struct role_types_data { char *role; struct string_list *types; }; struct type_transition_data { struct string_list *sources; struct string_list *targets; struct string_list *object_classes; char *default_type; char *name; enum tt_flavor flavor; }; struct role_transition_data { struct string_list *sources; struct string_list *targets; struct string_list *object_classes; char *default_role; }; struct if_call_data { char *name; struct string_list *args; }; struct if_call_list { struct if_call_data *call; struct if_call_list *next; }; struct declaration_data { enum decl_flavor flavor; char *name; struct string_list *attrs; }; struct decl_list { struct declaration_data *decl; struct decl_list *next; }; struct sel_context { int has_gen_context; // 1 if context is wrapped in gen_context, 0 if not char *user; char *role; char *type; char *range; }; struct fc_entry { char *path; char obj; struct sel_context *context; }; struct attribute_data { char *type; struct string_list *attrs; enum attr_flavor flavor; }; struct gen_require_data { unsigned char unquoted; }; struct cond_declaration_data { struct string_list *identifiers; }; union node_data { struct header_data *h_data; struct av_rule_data *av_data; struct xav_rule_data *xav_data; struct role_allow_data *ra_data; struct role_types_data *rtyp_data; struct type_transition_data *tt_data; struct role_transition_data *rt_data; struct if_call_data *ic_data; struct declaration_data *d_data; struct fc_entry *fc_data; struct attribute_data *at_data; struct gen_require_data *gr_data; struct cond_declaration_data *cd_data; char *str; }; struct policy_node { struct policy_node *parent; struct policy_node *next; struct policy_node *prev; struct policy_node *first_child; enum node_flavor flavor; union node_data data; char *exceptions; unsigned int lineno; }; enum selint_error insert_policy_node_child(struct policy_node *parent, enum node_flavor flavor, union node_data data, unsigned int lineno); enum selint_error insert_policy_node_next(struct policy_node *prev, enum node_flavor flavor, union node_data data, unsigned int lineno); // Returns 1 if the node is a template call, and 0 if not int is_template_call(const struct policy_node *node); const char *get_name_if_in_template(const struct policy_node *cur); struct name_list *get_names_in_node(const struct policy_node *node); struct name_list *get_names_required(const struct policy_node *node); const char *decl_flavor_to_string(enum decl_flavor flavor); /********************************** * Return 1 if the node is in a require block * and 0 otherwise **********************************/ int is_in_require(const struct policy_node *cur); /********************************** * Return 1 if the node is in an interface definition block * and 0 otherwise * Note, that a template definition is *not* an interface definition * for the purpose of this check **********************************/ int is_in_if_define(const struct policy_node *cur); //Return the next node in a depth first search of the tree struct policy_node *dfs_next(const struct policy_node *node); enum selint_error free_policy_node(struct policy_node *to_free); enum selint_error free_header_data(struct header_data *to_free); enum selint_error free_av_rule_data(struct av_rule_data *to_free); enum selint_error free_xav_rule_data(struct xav_rule_data *to_free); enum selint_error free_ra_data(struct role_allow_data *to_free); enum selint_error free_rtyp_data(struct role_types_data *to_free); enum selint_error free_type_transition_data(struct type_transition_data *to_free); enum selint_error free_role_transition_data(struct role_transition_data *to_free); enum selint_error free_if_call_data(struct if_call_data *to_free); enum selint_error free_declaration_data(struct declaration_data *to_free); enum selint_error free_decl_list(struct decl_list *to_free); // Only free the list, not what it's pointing to enum selint_error free_if_call_list(struct if_call_list *to_free); void free_fc_entry(struct fc_entry *to_free); void free_sel_context(struct sel_context *to_free); void free_attribute_data(struct attribute_data *to_free); void free_gen_require_data(struct gen_require_data *to_free); void free_cond_declaration_data(struct cond_declaration_data *to_free); #endif selint-1.5.1/src/util.c000066400000000000000000000023431475050262500147340ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "util.h" int verbose_flag; void print_if_verbose(const char *format, ...) { if (!verbose_flag) { return; } va_list args; va_start(args, format); vprintf(format, args); va_end(args); } bool ends_with(const char *str, size_t str_len, const char *suffix, size_t suffix_len) { if (str_len < suffix_len) { return 0; } return (0 == strncmp(str + str_len - suffix_len, suffix, suffix_len)); } char* trim_right(char *str) { size_t len = strlen(str); while (len > 0 && isspace((unsigned char)str[len-1])) { str[len-1] = '\0'; len--; } return str; } selint-1.5.1/src/util.h000066400000000000000000000100241475050262500147340ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef UTIL_H #define UTIL_H #include #include #include // ignore conversions discarding const specifier, e.g. // const char [] -> char * // const char *[2]' -> char *const * // necessary for calling fts_open() and initializing cfg_opt_t #ifdef __clang__ #define IGNORE_CONST_DISCARD_BEGIN _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wincompatible-pointer-types-discards-qualifiers\"") #define IGNORE_CONST_DISCARD_END _Pragma("clang diagnostic pop") #elif defined(__GNUC__) #define IGNORE_CONST_DISCARD_BEGIN _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wdiscarded-qualifiers\"") \ _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") #define IGNORE_CONST_DISCARD_END _Pragma("GCC diagnostic pop") #else #define IGNORE_CONST_DISCARD_BEGIN #define IGNORE_CONST_DISCARD_END #endif __attribute__((format(printf,1,2))) void print_if_verbose(const char *format, ...); /**************************************************** * Checks whether a string ends with a suffix. * str - The string to check. * str_len - The length of the string to check. * suffix - The suffix to check against. * suffix_len - The length of the suffix to check against. * Returns true if the string ends with the given suffi, else false. ****************************************************/ bool ends_with(const char *str, size_t str_len, const char *suffix, size_t suffix_len); /**************************************************** * Remove whitespaces of the end of a string. * str - The string to trim. * Return the passed and now trimmed string. ****************************************************/ char* trim_right(char *str); /**************************************************** * * Conventions in refpolicy style policies * ****************************************************/ static const char *const RefPol_module_abbreviations[][2] = { { "fs" , "filesystem" }, { "corecmd" , "corecommands" }, { "seutil" , "selinuxutil" }, { "libs" , "libraries" }, { "dev" , "devices" }, { "term" , "terminal" }, { "corenet" , "corenetwork" }, { "auth" , "authlogin" }, { "userdom" , "userdomain" }, { "sysnet" , "sysnetwork" }, }; static const char *const RefPol_macros_with_module_prefix[] = { "domain_auto_transition_pattern", "domain_transition_pattern", }; /********************************** * Since the refpolicy style guide doesn't define what a "transform * interface" is, we use some heuristics. This checks if the interface * name ends with one of several strings used in interfaces that appear * to be what the style guide intends **********************************/ static const char *const RefPol_interface_transforming_suffixes[] = { "_type", "_file", "_domain", "_node", // Next three are found in mta module "_agent", "_delivery", "_sender", "_boolean", "_content", "_constrained", "_executable", "_exemption", "_object", "_mountpoint", }; static inline bool is_transform_interface(const char *if_name) { const char *suffix = strrchr(if_name, '_'); if (!suffix) { return false; } for (size_t i = 0; i < (sizeof RefPol_interface_transforming_suffixes / sizeof *RefPol_interface_transforming_suffixes); i++) { if (0 == strcmp(suffix, RefPol_interface_transforming_suffixes[i])) { return true; } } return false; } #endif selint-1.5.1/src/xalloc.h000066400000000000000000000052671475050262500152560ustar00rootroot00000000000000/* * Copyright 2022 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef XALLOC_H #define XALLOC_H #include #include #define oom_failure() \ do { \ fprintf(stderr, \ "Failed to allocate memory [%s():%d]\n", \ __func__, \ __LINE__); \ exit(EX_OSERR); \ } while(0) /********************************************* * Checked malloc wrapper. *********************************************/ #define xmalloc(size) ({ \ void *ret_ = malloc(size); \ if (!ret_) { \ oom_failure(); \ } \ ret_; \ }) /********************************************* * Checked calloc wrapper. *********************************************/ #define xcalloc(nmemb, size) ({ \ void *ret_ = calloc(nmemb, size); \ if (!ret_) { \ oom_failure(); \ } \ ret_; \ }) /********************************************* * Checked realloc wrapper. *********************************************/ #define xrealloc(ptr, size) ({ \ void *ret_ = realloc(ptr, size); \ if (!ret_) { \ oom_failure(); \ } \ ret_; \ }) /********************************************* * Checked strdup wrapper. *********************************************/ #define xstrdup(str) ({ \ void *ret_ = strdup(str); \ if (!ret_) { \ oom_failure(); \ } \ ret_; \ }) /********************************************* * Checked strndup wrapper. *********************************************/ #define xstrndup(str, size) ({ \ void *ret_ = strndup(str, size); \ if (!ret_) { \ oom_failure(); \ } \ ret_; \ }) #endif /* XALLOC_H */ selint-1.5.1/tests/000077500000000000000000000000001475050262500141645ustar00rootroot00000000000000selint-1.5.1/tests/Makefile.am000066400000000000000000000477731475050262500162420ustar00rootroot00000000000000# Copyright 2019 Tresys Technology, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @VALGRIND_CHECK_RULES@ VALGRIND_memcheck_FLAGS=--leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all TESTS = check_tree check_parse_functions check_maps check_parsing check_parse_fc check_template check_file_list check_fc_checks check_check_hooks check_selint_config check_if_checks check_string_list check_runner check_startup check_te_checks check_ordering check_perm_macro check_name_list check_PROGRAMS = ${TESTS} AV_FILE_PERM_FILES=sample_av/file/index \ sample_av/file/perms/append \ sample_av/file/perms/audit_access \ sample_av/file/perms/create \ sample_av/file/perms/entrypoint \ sample_av/file/perms/execmod \ sample_av/file/perms/execute \ sample_av/file/perms/execute_no_trans \ sample_av/file/perms/getattr \ sample_av/file/perms/ioctl \ sample_av/file/perms/link \ sample_av/file/perms/lock \ sample_av/file/perms/map \ sample_av/file/perms/mounton \ sample_av/file/perms/open \ sample_av/file/perms/quotaon \ sample_av/file/perms/read \ sample_av/file/perms/relabelfrom \ sample_av/file/perms/relabelto \ sample_av/file/perms/rename \ sample_av/file/perms/setattr \ sample_av/file/perms/swapon \ sample_av/file/perms/unlink \ sample_av/file/perms/write AV_SOCKET_PERM_FILES=sample_av/socket/index \ sample_av/socket/perms/accept \ sample_av/socket/perms/append \ sample_av/socket/perms/bind \ sample_av/socket/perms/connect \ sample_av/socket/perms/create \ sample_av/socket/perms/getattr \ sample_av/socket/perms/getopt \ sample_av/socket/perms/ioctl \ sample_av/socket/perms/listen \ sample_av/socket/perms/lock \ sample_av/socket/perms/map \ sample_av/socket/perms/name_bind \ sample_av/socket/perms/read \ sample_av/socket/perms/recvfrom \ sample_av/socket/perms/recv_msg \ sample_av/socket/perms/relabelfrom \ sample_av/socket/perms/relabelto \ sample_av/socket/perms/send_msg \ sample_av/socket/perms/sendto \ sample_av/socket/perms/setattr \ sample_av/socket/perms/setopt \ sample_av/socket/perms/shutdown \ sample_av/socket/perms/write AV_X_CURSOR_PERM_FILES=sample_av/x_cursor/index \ sample_av/x_cursor/perms/create \ sample_av/x_cursor/perms/destroy \ sample_av/x_cursor/perms/getattr \ sample_av/x_cursor/perms/read \ sample_av/x_cursor/perms/setattr \ sample_av/x_cursor/perms/use \ sample_av/x_cursor/perms/write SAMPLE_CONFIG_FILES=sample_configs/bad_format_2.conf \ sample_configs/bad_format.conf \ sample_configs/check_config.conf \ sample_configs/invalid_option.conf \ sample_configs/severity_convention.conf \ sample_configs/severity_error.conf \ sample_configs/severity_fatal.conf \ sample_configs/severity_invalid.conf \ sample_configs/severity_style.conf \ sample_configs/severity_warning.conf \ sample_configs/bad_order.conf \ sample_configs/refpolicy_ordering.conf \ sample_configs/order_requires.conf SAMPLE_POLICY_FILES=sample_policy_files/access_vectors \ sample_policy_files/bad_modules.conf \ sample_policy_files/bad_obj_perm_sets.spt \ sample_policy_files/bad_role_allow.te \ sample_policy_files/basic.fc \ sample_policy_files/basic.if \ sample_policy_files/basic.te \ sample_policy_files/blocks.te \ sample_policy_files/bool_declarations.te \ sample_policy_files/declaring_template.if \ sample_policy_files/declaring_template.te \ sample_policy_files/disable_booltunable.te \ sample_policy_files/disable_comment.if \ sample_policy_files/disable_comment.te \ sample_policy_files/disable_require.if \ sample_policy_files/empty.te \ sample_policy_files/extended_perms.te \ sample_policy_files/ifdef.if \ sample_policy_files/ifdef_block.te \ sample_policy_files/modules.conf \ sample_policy_files/nested_templates.if \ sample_policy_files/none_context.fc \ sample_policy_files/obj_perm_sets.spt \ sample_policy_files/perms.spt \ sample_policy_files/syntax_error.te \ sample_policy_files/uncommon.te \ sample_policy_files/with_m4.fc FUNCTIONAL_TEST_FILES=functional/end-to-end.bats \ functional/configs/bad_ids.conf \ functional/configs/broken.conf \ functional/configs/default.conf \ functional/configs/empty.conf \ functional/configs/fc_macros.conf \ functional/configs/order_ref.conf \ functional/configs/order_lax.conf \ functional/policies/check_triggers/access_vectors \ functional/policies/check_triggers/c04.if \ functional/policies/check_triggers/c05.if \ functional/policies/check_triggers/c05.te \ functional/policies/check_triggers/c06.pass.if \ functional/policies/check_triggers/c06.warn.if \ functional/policies/check_triggers/c07.te \ functional/policies/check_triggers/c07.if \ functional/policies/check_triggers/c08.te \ functional/policies/check_triggers/c08_other.te \ functional/policies/check_triggers/e02.fc \ functional/policies/check_triggers/e03e04e05.fc \ functional/policies/check_triggers/e06.te \ functional/policies/check_triggers/e06.if \ functional/policies/check_triggers/e07.pass.te \ functional/policies/check_triggers/e07.warn.te \ functional/policies/check_triggers/e08.pass.te \ functional/policies/check_triggers/e08.warn.te \ functional/policies/check_triggers/e09.te \ functional/policies/check_triggers/e10.pass.te \ functional/policies/check_triggers/e10.warn.te \ functional/policies/check_triggers/modules.conf \ functional/policies/check_triggers/obj_perm_sets.spt \ functional/policies/check_triggers/security_classes \ functional/policies/check_triggers/s01.te \ functional/policies/check_triggers/s02.fc \ functional/policies/check_triggers/s02_other.te \ functional/policies/check_triggers/s03.te \ functional/policies/check_triggers/s04.if \ functional/policies/check_triggers/s05.if \ functional/policies/check_triggers/s06.te \ functional/policies/check_triggers/s07.fc \ functional/policies/check_triggers/s08.if \ functional/policies/check_triggers/s09.pass.te \ functional/policies/check_triggers/s09.warn.te \ functional/policies/check_triggers/w01_other.te \ functional/policies/check_triggers/w01.te \ functional/policies/check_triggers/w02.if \ functional/policies/check_triggers/w02_role.if \ functional/policies/check_triggers/w02_role.te \ functional/policies/check_triggers/w02.te \ functional/policies/check_triggers/w02.bad_if.if \ functional/policies/check_triggers/w02_system_nowarn.if \ functional/policies/check_triggers/w02_system_warn.if \ functional/policies/check_triggers/w03_alias.if \ functional/policies/check_triggers/w03_system_nowarn.if \ functional/policies/check_triggers/w03_system_warn.if \ functional/policies/check_triggers/w03.if \ functional/policies/check_triggers/w03_role.if \ functional/policies/check_triggers/w03_stub.if \ functional/policies/check_triggers/w03_ta.if \ functional/policies/check_triggers/w04.fc \ functional/policies/check_triggers/w05_other.if \ functional/policies/check_triggers/w05.te \ functional/policies/check_triggers/w06.if \ functional/policies/check_triggers/w07.if \ functional/policies/check_triggers/w07.0.te \ functional/policies/check_triggers/w07.1.te \ functional/policies/check_triggers/w08.1.te \ functional/policies/check_triggers/w08.2.te \ functional/policies/check_triggers/w09.te \ functional/policies/check_triggers/w10.pass.te \ functional/policies/check_triggers/w10.warn.te \ functional/policies/check_triggers/w11.te \ functional/policies/check_triggers/w11.if \ functional/policies/check_triggers/w12.te \ functional/policies/check_triggers/w13.te \ functional/policies/check_triggers/x01.if \ functional/policies/check_triggers/x01.te \ functional/policies/check_triggers/x02.te \ functional/policies/check_triggers/C-001/interleaved.expect.ref \ functional/policies/check_triggers/C-001/interleaved.expect.lax \ functional/policies/check_triggers/C-001/interleaved.te \ functional/policies/check_triggers/C-001/interleaved2.expect.ref \ functional/policies/check_triggers/C-001/interleaved2.expect.lax \ functional/policies/check_triggers/C-001/interleaved2.te \ functional/policies/check_triggers/C-001/if_in_optional.expect.ref \ functional/policies/check_triggers/C-001/if_in_optional.expect.lax \ functional/policies/check_triggers/C-001/if_in_optional.te \ functional/policies/check_triggers/C-001/kernel_module_first.expect.ref \ functional/policies/check_triggers/C-001/kernel_module_first.expect.lax \ functional/policies/check_triggers/C-001/kernel_module_first.te \ functional/policies/check_triggers/C-001/optional.expect.ref \ functional/policies/check_triggers/C-001/optional.expect.lax \ functional/policies/check_triggers/C-001/optional.te \ functional/policies/check_triggers/C-001/optional_optional.expect.ref \ functional/policies/check_triggers/C-001/optional_optional.expect.lax \ functional/policies/check_triggers/C-001/optional_optional.te \ functional/policies/check_triggers/C-001/role_ifs.expect.ref \ functional/policies/check_triggers/C-001/role_ifs.expect.lax \ functional/policies/check_triggers/C-001/role_ifs.te \ functional/policies/check_triggers/C-001/self_macro.expect.ref \ functional/policies/check_triggers/C-001/self_macro.expect.lax \ functional/policies/check_triggers/C-001/self_macro.te \ functional/policies/check_triggers/C-001/simple.expect.ref \ functional/policies/check_triggers/C-001/simple.expect.lax \ functional/policies/check_triggers/C-001/simple.te \ functional/policies/check_triggers/C-001/types_in_requires.expect.ref \ functional/policies/check_triggers/C-001/types_in_requires.expect.lax \ functional/policies/check_triggers/C-001/types_in_requires.te \ functional/policies/check_triggers/C-001/decl_in_block.expect.ref \ functional/policies/check_triggers/C-001/decl_in_block.expect.lax \ functional/policies/check_triggers/C-001/decl_in_block.te \ functional/policies/check_triggers/C-001/interfaces/kernel/domain.if \ functional/policies/check_triggers/C-001/interfaces/kernel/kernel.if \ functional/policies/check_triggers/C-001/interfaces/other/mta.if \ functional/policies/check_triggers/C-001/interfaces/other/role_ifs.if \ functional/policies/check_triggers/C-001/interfaces/system/logging.if \ functional/policies/check_triggers/C-001/interfaces/system/init.if \ functional/policies/context/context.if \ functional/policies/context/context.te \ functional/policies/context2/context2.if \ functional/policies/context2/context2.te \ functional/policies/misc/disable.if \ functional/policies/misc/disable_multiple_other.te \ functional/policies/misc/disable_multiple.te \ functional/policies/misc/disable_require_decl.if \ functional/policies/misc/disable_require_start.te \ functional/policies/misc/disable.te \ functional/policies/misc/fc_macros.fc \ functional/policies/misc/needs_context.te \ functional/policies/misc/nesting.if \ functional/policies/misc/nesting.te \ functional/policies/misc/no_issues.te \ functional/policies/parse_errors/test1.output \ functional/policies/parse_errors/test1.te \ functional/policies/parse_errors/test2.output \ functional/policies/parse_errors/test2.te \ functional/policies/parse_errors/test3.output \ functional/policies/parse_errors/test4.output \ functional/policies/parse_errors/test4.te \ functional/policies/parse_errors/test5.output \ functional/policies/parse_errors/test6.output \ functional/policies/parse_errors/test7.if \ functional/policies/parse_errors/test7.output \ functional/policies/parse_errors/test8.te \ functional/policies/parse_errors/test8.output \ functional/policies/parse_errors/test9.te \ functional/policies/parse_errors/test9.output \ functional/policies/report_format/test1.output \ functional/policies/report_format/test1.output.full \ functional/policies/report_format/test1.output.summary \ functional/policies/report_format/test1.output.summaryonly \ functional/policies/report_format/test1.output.warn \ functional/policies/report_format/test1.te EXTRA_DIST = ${AV_FILE_PERM_FILES} ${AV_SOCKET_PERM_FILES} ${AV_X_CURSOR_PERM_FILES} ${SAMPLE_CONFIG_FILES} ${SAMPLE_POLICY_FILES} ${FUNCTIONAL_TEST_FILES} AM_CFLAGS += @CHECK_CFLAGS@ -DSAMPLE_POL_DIR="\"$(srcdir)/sample_policy_files/\"" -DSAMPLE_CONF_DIR="\"$(srcdir)/sample_configs/\"" -DSAMPLE_AV_DIR="\"$(srcdir)/sample_av/\"" if COND_GCOV AM_CFLAGS += --coverage -fno-inline -fno-inline-small-functions -fno-default-inline endif TEST_UTILS_HEADS=test_utils.h $(top_builddir)/src/tree.h $(top_builddir)/src/string_list.h # Below does not include test_utils.o, because that will be built by the # inclusion of test_utils.c in SOURCES for each program needing test_utils, # so this only includes the additional object files to link against TEST_UTILS_OBJS=$(top_builddir)/src/tree.o $(top_builddir)/src/string_list.o UTIL_HEADS=$(top_builddir)/src/util.h UTIL_OBJS=$(top_builddir)/src/util.o SELINT_ERROR_HEADS=$(top_builddir)/src/selint_error.h STRING_LIST_HEADS=$(top_builddir)/src/string_list.h ${SELINT_ERROR_HEADS} STRING_LIST_OBJS=$(top_builddir)/src/string_list.o NAME_LIST_HEADS=$(top_builddir)/src/name_list.h NAME_LIST_OBJS=$(top_builddir)/src/name_list.o ${STRING_LIST_OBJS} COLOR_HEADS=$(top_builddir)/src/color.h COLOR_OBJS=$(top_builddir)/src/color.o PERM_MACRO_HEADS=$(top_builddir)/src/perm_macro.h ${UTIL_HEADS} ${COLOR_HEADS} PERM_MACRO_OBJS=$(top_builddir)/src/perm_macro.o ${UTIL_OBJS} ${COLOR_OBJS} SELINT_CONFIG_HEADS=$(top_builddir)/src/selint_config.h ${STRING_LIST_HEADS} ${TREE_HEADS} ${MAPS_HEADS} ${ORDERING_HEADS} SELINT_CONFIG_OBJS=$(top_builddir)/src/selint_config.o ${STRING_LIST_OBJS} ${TREE_OBJS} ${MAPS_OBJS} ${UTIL_OBJS} TREE_HEADS=$(top_builddir)/src/tree.h ${STRING_LIST_HEADS} ${NAME_LIST_HEADS} TREE_OBJS=$(top_builddir)/src/tree.o ${NAME_LIST_OBJS} $(top_builddir)/src/maps.o FILE_LIST_HEADS=$(top_builddir)/src/file_list.h ${TREE_HEADS} FILE_LIST_OBJS=$(top_builddir)/src/file_list.o ${TREE_OBJS} MAPS_HEADS=$(top_builddir)/src/maps.h ${SELINT_ERROR_HEADS} ${TREE_HEADS} MAPS_OBJS=$(top_builddir)/src/maps.o ${TREE_OBJS} TEMPLATE_HEADS=$(top_builddir)/src/template.h ${SELINT_ERROR_HEADS} ${TREE_HEADS} TEMPLATE_OBJS=$(top_builddir)/src/template.o ${TREE_OBJS} PARSE_FUNCTIONS_HEADS=$(top_builddir)/src/parse_functions.h ${SELINT_ERROR_HEADS} ${TREE_HEADS} ${MAPS_HEADS} ${PERM_MACRO_HEADS} PARSE_FUNCTIONS_OBJS=$(top_builddir)/src/parse_functions.o ${TEMPLATE_OBJS} ${ORDERING_OBJS} ${PERM_MACRO_OBJS} PARSE_HEADS=$(top_builddir)/src/parse.h ${PARSE_FUNCTIONS_HEADS} PARSE_OBJS=$(top_builddir)/src/parse.o $(top_builddir)/src/lex.o ${CHECK_HOOKS_OBJS} ${PARSE_FUNCTIONS_OBJS} STARTUP_HEADS=$(top_builddir)/src/startup.h ${SELINT_ERROR_HEADS} ${FILE_LIST_HEADS} ${PARSE_HEADS} STARTUP_OBJS=$(top_builddir)/src/startup.o ${FILE_LIST_OBJS} ${PARSE_OBJS} PARSE_FC_HEADS = $(top_builddir)/src/parse_fc.h $(TREE_HEADS) PARSE_FC_OBJS = $(top_builddir)/src/parse_fc.o $(TREE_OBJS) CHECK_HOOKS_HEADS=$(top_builddir)/src/check_hooks.h ${COLOR_HEADS} ${SELINT_ERROR_HEADS} ${TREE_HEADS} CHECK_HOOKS_OBJS=$(top_builddir)/src/check_hooks.o ${COLOR_OBJS} ${TREE_OBJS} FC_CHECKS_HEADS=$(top_builddir)/src/fc_checks.h ${CHECK_HOOKS_HEADS} FC_CHECKS_OBJS=$(top_builddir)/src/fc_checks.o ${CHECK_HOOKS_OBJS} IF_CHECKS_HEADS=$(top_builddir)/src/if_checks.h ${CHECK_HOOKS_HEADS} ${UTIL_HEADS} IF_CHECKS_OBJS=$(top_builddir)/src/if_checks.o ${CHECK_HOOKS_OBJS} ${UTIL_OBJS} TE_CHECKS_HEADS=$(top_builddir)/src/te_checks.h ${CHECK_HOOKS_HEADS} ${UTIL_HEADS} TE_CHECKS_OBJS=$(top_builddir)/src/te_checks.o ${CHECK_HOOKS_OBJS} $(top_builddir)/src/ordering.o ${UTIL_OBJS} RUNNER_HEADS=$(top_builddir)/src/runner.h ${SELINT_ERROR_HEADS} ${CHECK_HOOKS_HEADS} ${PARSE_FUNCTIONS_HEADS} ${FILE_LIST_HEADS} RUNNER_OBJS=$(top_builddir)/src/runner.o ${CHECK_HOOKS_OBJS} ${PARSE_FUNCTIONS_OBJS} ${FILE_LIST_OBJS} ${FC_CHECKS_OBJS} ${IF_CHECKS_OBJS} ${TE_CHECKS_OBJS} ${PARSE_FC_OBJS} ${UTIL_OBJS} ${STARTUP_OBJS} ${PARSE_OBJS} ORDERING_HEADS=$(top_builddir)/src/ordering.h ${SELINT_ERROR_HEADS} ${TREE_HEADS} ORDERING_OBJS=$(top_builddir)/src/ordering.o ${TREE_OBJS} ${MAPS_OBJS} check_string_list_SOURCES = check_string_list.c ${STRING_LIST_HEADS} check_string_list_LDADD = @CHECK_LIBS@ $(sort ${STRING_LIST_OBJS}) check_name_list_SOURCES = check_name_list.c ${NAME_LIST_HEADS} check_name_list_LDADD = @CHECK_LIBS@ $(sort ${NAME_LIST_OBJS}) check_selint_config_SOURCES = check_selint_config.c ${SELINT_CONFIG_HEADS} ${STRING_LIST_HEADS} check_selint_config_LDADD = @CHECK_LIBS@ $(sort ${SELINT_CONFIG_OBJS} ${STRING_LIST_OBJS}) check_maps_SOURCES = check_maps.c ${MAPS_HEADS} check_maps_LDADD = @CHECK_LIBS@ $(sort ${MAPS_OBJS}) check_startup_SOURCES = check_startup.c ${STARTUP_HEADS} ${MAPS_HEADS} ${SELINT_ERROR_HEADS} check_startup_LDADD = @CHECK_LIBS@ $(sort ${STARTUP_OBJS} ${MAPS_OBJS}) check_tree_SOURCES = check_tree.c test_utils.c ${TREE_HEADS} ${TEST_UTILS_HEADS} check_tree_LDADD = @CHECK_LIBS@ $(sort ${TREE_OBJS} ${MAPS_OBJS} ${TEST_UTILS_OBJS}) check_file_list_SOURCES = check_file_list.c ${FILE_LIST_HEADS} check_file_list_LDADD = @CHECK_LIBS@ $(sort ${FILE_LIST_OBJS}) check_parse_functions_SOURCES = check_parse_functions.c ${PARSE_FUNCTIONS_HEADS} ${MAPS_HEADS} ${PARSE_HEADS} ${SELINT_ERROR_HEADS} check_parse_functions_LDADD = @CHECK_LIBS@ $(sort ${PARSE_FUNCTIONS_OBJS} ${MAPS_OBJS} ${PARSE_OBJS}) check_parsing_SOURCES = check_parsing.c ${PARSE_HEADS} ${TREE_HEADS} ${PARSE_FUNCTIONS_HEADS} ${SELINT_ERROR_HEADS} check_parsing_LDADD = @CHECK_LIBS@ $(sort ${PARSE_OBJS} ${TREE_OBJS} ${PARSE_FUNCTIONS_OBJS}) check_parse_fc_SOURCES = check_parse_fc.c ${PARSE_FC_HEADS} ${TREE_HEADS} check_parse_fc_LDADD = @CHECK_LIBS@ $(sort ${PARSE_FC_OBJS} ${TREE_OBJS}) check_template_SOURCES = check_template.c ${PARSE_FUNCTIONS_HEADS} ${TEMPLATE_HEADS} ${PARSE_HEADS} ${MAPS_HEADS} check_template_LDADD = @CHECK_LIBS@ $(sort ${PARSE_FUNCTIONS_OBJS} ${TEMPLATE_OBJS} ${PARSE_OBJS} ${MAPS_OBJS}) check_check_hooks_SOURCES = check_check_hooks.c ${CHECK_HOOKS_HEADS} check_check_hooks_LDADD = @CHECK_LIBS@ $(sort ${CHECK_HOOKS_OBJS}) check_fc_checks_SOURCES = check_fc_checks.c ${FC_CHECKS_HEADS} ${CHECK_HOOKS_HEADS} ${MAPS_HEADS} check_fc_checks_LDADD = @CHECK_LIBS@ $(sort ${FC_CHECKS_OBJS} ${CHECK_HOOKS_OBJS} ${MAPS_OBJS} ${UTIL_OBJS}) check_if_checks_SOURCES = check_if_checks.c test_utils.c ${IF_CHECKS_HEADS} ${CHECK_HOOKS_HEADS} ${MAPS_HEADS} ${TEST_UTILS_HEADS} check_if_checks_LDADD = @CHECK_LIBS@ $(sort ${IF_CHECKS_OBJS} ${CHECK_HOOKS_OBJS} ${MAPS_OBJS} ${TEST_UTILS_OBJS}) check_te_checks_SOURCES = check_te_checks.c test_utils.c ${TE_CHECKS_HEADS} ${CHECK_HOOKS_HEADS} ${MAPS_HEADS} ${TEST_UTILS_HEADS} ${PERM_MACRO_HEADS} check_te_checks_LDADD = @CHECK_LIBS@ $(sort ${TE_CHECKS_OBJS} ${CHECK_HOOKS_OBJS} ${MAPS_OBJS} ${TEST_UTILS_OBJS} ${PERM_MACRO_OBJS}) check_runner_SOURCES = check_runner.c ${STRING_LIST_HEADS} ${RUNNER_HEADS} check_runner_LDADD = @CHECK_LIBS@ $(sort ${STRING_LIST_OBJS} ${RUNNER_OBJS}) check_ordering_SOURCES = check_ordering.c ${ORDERING_HEADS} ${RUNNER_HEADS} ${MAPS_HEADS} check_ordering_LDADD = @CHECK_LIBS@ $(sort ${ORDERING_OBJS} ${RUNNER_OBJS} ${MAPS_OBJS}) check_perm_macro_SOURCES = check_perm_macro.c ${PERM_MACRO_HEADS} ${STARTUP_HEADS} ${SELINT_ERROR_HEADS} ${MAPS_HEADS} check_perm_macro_LDADD = @CHECK_LIBS@ $(sort ${PERM_MACRO_OBJS} ${STARTUP_OBJS} ${MAPS_OBJS}) MOSTLYCLEANFILES = *.gcov *.gcda *.gcno functional/policies/parse_errors/test3_tmp.if functional/policies/parse_errors/test5_tmp.te functional/policies/parse_errors/test6_tmp.if selint-1.5.1/tests/check_check_hooks.c000066400000000000000000000165261475050262500177570ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../src/check_hooks.h" int check_called; int check2_called; struct check_result * example_check(const struct check_data *check_data, const struct policy_node *node); struct check_result * example_check2(const struct check_data *check_data, const struct policy_node *node); struct check_result * returns_blank_result(const struct check_data *check_data, const struct policy_node *node); struct check_result * example_check(__attribute__((unused)) const struct check_data *check_data, __attribute__((unused)) const struct policy_node *node) { check_called = 1; return (struct check_result *) NULL; } struct check_result * example_check2(__attribute__((unused)) const struct check_data *check_data, __attribute__((unused)) const struct policy_node *node) { check2_called = 1; return (struct check_result *) NULL; } struct check_result * returns_blank_result(__attribute__((unused)) const struct check_data *check_data, __attribute__((unused)) const struct policy_node *node) { return calloc(1, sizeof(struct check_result)); } START_TEST (test_add_check) { struct checks *ck = calloc(1, sizeof(struct checks)); check_called = 0; ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_AV_RULE, ck, "E-999", example_check)); ck_assert_ptr_nonnull(ck->check_nodes[NODE_AV_RULE]); ck_assert_ptr_null(ck->check_nodes[NODE_FC_ENTRY]); ck_assert_ptr_null(ck->check_nodes[NODE_ERROR]); ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_TT_RULE, ck, "E-999", example_check)); ck_assert_ptr_nonnull(ck->check_nodes[NODE_AV_RULE]); ck_assert_ptr_nonnull(ck->check_nodes[NODE_TT_RULE]); ck_assert_ptr_null(ck->check_nodes[NODE_FC_ENTRY]); ck_assert_ptr_null(ck->check_nodes[NODE_ERROR]); ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_DECL, ck, "E-999", example_check)); ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_INTERFACE_DEF, ck, "E-999", example_check)); ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_TEMP_DEF, ck, "E-999", example_check)); ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_IF_CALL, ck, "E-999", example_check)); ck_assert_ptr_nonnull(ck->check_nodes[NODE_DECL]); ck_assert_ptr_nonnull(ck->check_nodes[NODE_INTERFACE_DEF]); ck_assert_ptr_nonnull(ck->check_nodes[NODE_TEMP_DEF]); ck_assert_ptr_nonnull(ck->check_nodes[NODE_IF_CALL]); ck_assert_ptr_null(ck->check_nodes[NODE_FC_ENTRY]); ck_assert_ptr_null(ck->check_nodes[NODE_ERROR]); ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_FC_ENTRY, ck, "E-999", example_check)); ck_assert_ptr_nonnull(ck->check_nodes[NODE_AV_RULE]); ck_assert_ptr_nonnull(ck->check_nodes[NODE_FC_ENTRY]); ck_assert_ptr_null(ck->check_nodes[NODE_ERROR]); ck_assert_ptr_eq(ck->check_nodes[NODE_FC_ENTRY]->check_function, example_check); ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_ERROR, ck, "E-999", example_check)); ck_assert_ptr_nonnull(ck->check_nodes[NODE_ERROR]); ck_assert_int_eq(0, check_called); free_checks(ck); } END_TEST START_TEST (test_call_checks) { struct checks *ck = malloc(sizeof(struct checks)); memset(ck, 0, sizeof(struct checks)); check_called = 0; check2_called = 0; ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_AV_RULE, ck, "E-999", example_check)); struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_AV_RULE; ck_assert_int_eq(SELINT_SUCCESS, call_checks(ck, NULL, node)); ck_assert_int_eq(1, check_called); ck_assert_int_eq(0, check2_called); check_called = 0; ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_AV_RULE, ck, "E-999", example_check2)); ck_assert_int_eq(SELINT_SUCCESS, call_checks(ck, NULL, node)); ck_assert_int_eq(0, ck->check_nodes[NODE_AV_RULE]->issues_found); ck_assert_int_eq(0, ck->check_nodes[NODE_AV_RULE]->next->issues_found); ck_assert_int_eq(1, check_called); ck_assert_int_eq(1, check2_called); node->flavor = NODE_TT_RULE; check_called = 0; check2_called = 0; ck_assert_int_eq(SELINT_SUCCESS, call_checks(ck, NULL, node)); ck_assert_int_eq(0, ck->check_nodes[NODE_AV_RULE]->issues_found); ck_assert_int_eq(0, ck->check_nodes[NODE_AV_RULE]->next->issues_found); ck_assert_int_eq(0, check_called); ck_assert_int_eq(0, check2_called); free_policy_node(node); free_checks(ck); } END_TEST START_TEST (test_disable_check) { struct checks *ck = calloc(1, sizeof(struct checks)); check_called = 0; ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_AV_RULE, ck, "E-999", example_check)); struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_AV_RULE; ck_assert_int_eq(SELINT_SUCCESS, call_checks(ck, NULL, node)); ck_assert_int_eq(1, check_called); check_called = 0; node->exceptions = strdup("E-999\n"); ck_assert_int_eq(SELINT_SUCCESS, call_checks(ck, NULL, node)); ck_assert_int_eq(0, check_called); free_policy_node(node); free_checks(ck); } END_TEST START_TEST (test_is_valid_check) { ck_assert_int_eq(1, is_valid_check("W-001")); ck_assert_int_eq(1, is_valid_check("W-005")); ck_assert_int_eq(0, is_valid_check("W-107")); ck_assert_int_eq(0, is_valid_check("foobar")); ck_assert_int_eq(1, is_valid_check("C-001")); ck_assert_int_eq(1, is_valid_check("S-001")); ck_assert_int_eq(1, is_valid_check("E-001")); ck_assert_int_eq(1, is_valid_check("F-001")); ck_assert_int_eq(0, is_valid_check("Y-001")); ck_assert_int_eq(0, is_valid_check("C-101")); } END_TEST START_TEST (test_increment_issues) { struct checks *ck = calloc(1, sizeof(struct checks)); ck_assert_int_eq(SELINT_SUCCESS, add_check(NODE_AV_RULE, ck, "E-999", returns_blank_result)); struct check_data *data = calloc(1, sizeof(struct check_data)); data->filename = strdup("example.te"); ck_assert_int_eq(0, ck->check_nodes[NODE_AV_RULE]->issues_found); struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_AV_RULE; ck_assert_int_eq(SELINT_SUCCESS, call_checks(ck, data, node)); ck_assert_int_eq(1, ck->check_nodes[NODE_AV_RULE]->issues_found); ck_assert_int_eq(SELINT_SUCCESS, call_checks(ck, data, node)); ck_assert_int_eq(2, ck->check_nodes[NODE_AV_RULE]->issues_found); free_policy_node(node); free(data->filename); free(data); free_checks(ck); } END_TEST static Suite *check_hooks_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Check_hooks"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_add_check); tcase_add_test(tc_core, test_call_checks); tcase_add_test(tc_core, test_disable_check); tcase_add_test(tc_core, test_is_valid_check); tcase_add_test(tc_core, test_increment_issues); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = check_hooks_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_fc_checks.c000066400000000000000000000240011475050262500173720ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../src/fc_checks.h" #include "../src/check_hooks.h" #include "../src/maps.h" START_TEST (test_check_file_context_types_exist) { struct check_data *data = malloc(sizeof(struct check_data)); data->mod_name = strdup("foo"); data->flavor = FILE_FC_FILE; struct policy_node *node = malloc(sizeof(struct policy_node)); memset(node, 0, sizeof(struct policy_node)); node->flavor = NODE_FC_ENTRY; struct fc_entry *entry = malloc(sizeof(struct fc_entry)); memset(entry, 0, sizeof(struct fc_entry)); entry->context = malloc(sizeof(struct sel_context)); memset(entry->context, 0, sizeof(struct sel_context)); entry->context->type = strdup("foo_t"); node->data.fc_data = entry; struct check_result *res = check_file_context_types_exist(data, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'E'); ck_assert_int_eq(res->check_id, E_ID_FC_TYPE); ck_assert_ptr_nonnull(res->message); free_check_result(res); insert_into_decl_map("foo_t", "foo", DECL_TYPE); res = check_file_context_types_exist(data, node); ck_assert_ptr_null(res); free_all_maps(); free(data->mod_name); free(data); free_policy_node(node); } END_TEST START_TEST (test_check_file_context_types_exist_bad_flavor) { struct check_data *data = malloc(sizeof(struct check_data)); data->mod_name = strdup("foo"); data->flavor = FILE_FC_FILE; struct policy_node *node = malloc(sizeof(struct policy_node)); memset(node, 0, sizeof(struct policy_node)); node->flavor = NODE_TE_FILE; struct check_result *res = check_file_context_types_exist(data, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq('F', res->severity); ck_assert_int_eq(F_ID_INTERNAL, res->check_id); ck_assert_ptr_nonnull(res->message); free_check_result(res); free(data->mod_name); free(data); free_policy_node(node); } END_TEST START_TEST (test_check_file_context_types_in_mod) { struct check_data *data = malloc(sizeof(struct check_data)); data->filename = strdup("foo"); data->mod_name = strdup("foo"); data->flavor = FILE_FC_FILE; const struct config_check_data cfg = { ORDER_LAX, {}, true, true, NULL }; data->config_check_data = &cfg; struct policy_node *node = malloc(sizeof(struct policy_node)); memset(node, 0, sizeof(struct policy_node)); node->flavor = NODE_FC_ENTRY; struct fc_entry *entry = malloc(sizeof(struct fc_entry)); memset(entry, 0, sizeof(struct fc_entry)); entry->context = malloc(sizeof(struct sel_context)); memset(entry->context, 0, sizeof(struct sel_context)); entry->context->type = strdup("foo_t"); node->data.fc_data = entry; struct check_result *res = check_file_context_types_in_mod(data, node); ck_assert_ptr_null(res); insert_into_decl_map("foo_t", "bar", DECL_TYPE); res = check_file_context_types_in_mod(data, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'S'); ck_assert_int_eq(res->check_id, S_ID_FC_TYPE); ck_assert_ptr_nonnull(res->message); free_check_result(res); free(data->mod_name); data->mod_name = strdup("bar"); res = check_file_context_types_exist(data, node); ck_assert_ptr_null(res); free(res); free_all_maps(); free(data->mod_name); free(data->filename); free(data); free_policy_node(node); } END_TEST START_TEST (test_check_file_context_roles) { struct check_data *data = malloc(sizeof(struct check_data)); data->mod_name = strdup("foo"); data->flavor = FILE_FC_FILE; struct policy_node *node = malloc(sizeof(struct policy_node)); memset(node, 0, sizeof(struct policy_node)); node->flavor = NODE_FC_ENTRY; struct fc_entry *entry = malloc(sizeof(struct fc_entry)); memset(entry, 0, sizeof(struct fc_entry)); entry->context = malloc(sizeof(struct sel_context)); memset(entry->context, 0, sizeof(struct sel_context)); entry->context->role = strdup("object_r"); node->data.fc_data = entry; struct check_result *res = check_file_context_roles(data, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'E'); ck_assert_int_eq(res->check_id, E_ID_FC_ROLE); ck_assert_ptr_nonnull(res->message); free_check_result(res); insert_into_decl_map("object_r", "files", DECL_ROLE); res = check_file_context_roles(data, node); ck_assert_ptr_null(res); free(res); free_all_maps(); free(data->mod_name); free(data); free_policy_node(node); } END_TEST START_TEST (test_check_file_context_users) { struct check_data *data = malloc(sizeof(struct check_data)); data->mod_name = strdup("foo"); data->flavor = FILE_FC_FILE; struct policy_node *node = malloc(sizeof(struct policy_node)); memset(node, 0, sizeof(struct policy_node)); node->flavor = NODE_FC_ENTRY; struct fc_entry *entry = malloc(sizeof(struct fc_entry)); memset(entry, 0, sizeof(struct fc_entry)); entry->context = malloc(sizeof(struct sel_context)); memset(entry->context, 0, sizeof(struct sel_context)); entry->context->user = strdup("system_u"); node->data.fc_data = entry; struct check_result *res = check_file_context_users(data, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'E'); ck_assert_int_eq(res->check_id, E_ID_FC_USER); ck_assert_ptr_nonnull(res->message); free_check_result(res); insert_into_decl_map("system_u", "files", DECL_USER); res = check_file_context_users(data, node); ck_assert_ptr_null(res); free(res); free_all_maps(); free(data->mod_name); free(data); free_policy_node(node); } END_TEST START_TEST (test_check_file_context_error_nodes) { struct check_data *data = malloc(sizeof(struct check_data)); data->mod_name = strdup("foo"); data->flavor = FILE_FC_FILE; struct policy_node *node = malloc(sizeof(struct policy_node)); memset(node, 0, sizeof(struct policy_node)); node->flavor = NODE_FC_ENTRY; struct check_result *res = check_file_context_error_nodes(data, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'F'); ck_assert_int_eq(res->check_id, F_ID_INTERNAL); ck_assert_ptr_nonnull(res->message); free_check_result(res); node->flavor = NODE_ERROR; res = check_file_context_error_nodes(data, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'E'); ck_assert_int_eq(res->check_id, E_ID_FC_ERROR); ck_assert_ptr_nonnull(res->message); free_check_result(res); free(data->mod_name); free(data); free_policy_node(node); } END_TEST START_TEST (test_fc_checks_handle_null_context_fields) { struct check_data *data = malloc(sizeof(struct check_data)); data->filename = strdup("foo"); data->mod_name = strdup("foo"); data->flavor = FILE_FC_FILE; struct policy_node *node = malloc(sizeof(struct policy_node)); memset(node, 0, sizeof(struct policy_node)); node->flavor = NODE_FC_ENTRY; struct fc_entry *entry = malloc(sizeof(struct fc_entry)); memset(entry, 0, sizeof(struct fc_entry)); node->data.fc_data = entry; ck_assert_ptr_null(check_file_context_types_exist(data, node)); ck_assert_ptr_null(check_file_context_types_in_mod(data, node)); ck_assert_ptr_null(check_file_context_roles(data, node)); ck_assert_ptr_null(check_file_context_users(data, node)); free(data->filename); free(data->mod_name); free(data); free_policy_node(node); } END_TEST START_TEST (test_check_file_context_regex) { struct check_data *data = malloc(sizeof(struct check_data)); data->mod_name = strdup("foo"); data->flavor = FILE_FC_FILE; struct policy_node *node = malloc(sizeof(struct policy_node)); memset(node, 0, sizeof(struct policy_node)); node->flavor = NODE_FC_ENTRY; struct fc_entry *entry = malloc(sizeof(struct fc_entry)); memset(entry, 0, sizeof(struct fc_entry)); entry->context = malloc(sizeof(struct sel_context)); memset(entry->context, 0, sizeof(struct sel_context)); entry->path = strdup("path.with.unescpaed.dots"); node->data.fc_data = entry; struct check_result *res = check_file_context_regex(data, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'W'); ck_assert_int_eq(res->check_id, W_ID_FC_REGEX); ck_assert_ptr_nonnull(res->message); free_check_result(res); free(entry->path); entry->path = strdup("path\\.with\\.escaped\\.dots"); res = check_file_context_regex(data, node); ck_assert_ptr_null(res); free(entry->path); entry->path = strdup("brackets\\.are[.s.kipped.]\\."); res = check_file_context_regex(data, node); ck_assert_ptr_null(res); free(entry->path); entry->path = strdup("unclosed[bracket"); res = check_file_context_regex(data, node); ck_assert_ptr_null(res); free(entry->path); entry->path = strdup("escaped[brackets\\]in.brackets]"); res = check_file_context_regex(data, node); ck_assert_ptr_null(res); free(data->mod_name); free(data); free_policy_node(node); } END_TEST static Suite *fc_checks_suite(void) { Suite *s; TCase *tc_core; s = suite_create("FC_Checks"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_check_file_context_types_exist); tcase_add_test(tc_core, test_check_file_context_types_exist_bad_flavor); tcase_add_test(tc_core, test_check_file_context_types_in_mod); tcase_add_test(tc_core, test_check_file_context_roles); tcase_add_test(tc_core, test_check_file_context_users); tcase_add_test(tc_core, test_check_file_context_error_nodes); tcase_add_test(tc_core, test_check_file_context_regex); tcase_add_test(tc_core, test_fc_checks_handle_null_context_fields); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = fc_checks_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_file_list.c000066400000000000000000000063401475050262500174420ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../src/file_list.h" # START_TEST (test_file_list_push_back) { struct policy_node *ast1 = malloc(sizeof(struct policy_node)); memset(ast1, 0, sizeof(struct policy_node)); struct policy_node *ast2 = malloc(sizeof(struct policy_node)); memset(ast2, 0, sizeof(struct policy_node)); struct policy_node *ast3 = malloc(sizeof(struct policy_node)); memset(ast3, 0, sizeof(struct policy_node)); struct policy_node *ast4 = malloc(sizeof(struct policy_node)); memset(ast4, 0, sizeof(struct policy_node)); struct policy_file_list *list = malloc(sizeof(struct policy_file_list)); memset(list, 0, sizeof(struct policy_file_list)); file_list_push_back(list, make_policy_file("file1", ast1)); file_list_push_back(list, make_policy_file("file2", ast2)); file_list_push_back(list, make_policy_file("file3", ast3)); file_list_push_back(list, make_policy_file("file4", ast4)); ck_assert_ptr_eq(list->head->file->ast, ast1); ck_assert_str_eq(list->head->file->filename, "file1"); ck_assert_ptr_eq(list->head->next->file->ast, ast2); ck_assert_str_eq(list->head->next->file->filename, "file2"); ck_assert_ptr_eq(list->tail->file->ast, ast4); ck_assert_str_eq(list->tail->file->filename, "file4"); free_file_list(list); } END_TEST START_TEST (test_make_policy_file) { struct policy_file *file = make_policy_file("foo", NULL); ck_assert_ptr_nonnull(file); ck_assert_str_eq(file->filename, "foo"); ck_assert_ptr_null(file->ast); free(file->filename); free(file); } END_TEST START_TEST (test_file_name_in_file_list) { struct policy_file_list *list = calloc(1, sizeof(struct policy_file_list)); file_list_push_back(list, make_policy_file("foo", NULL)); file_list_push_back(list, make_policy_file("bar", NULL)); file_list_push_back(list, make_policy_file("baz", NULL)); ck_assert_int_eq(0, file_name_in_file_list("not_in_list", list)); ck_assert_int_eq(1, file_name_in_file_list("foo", list)); ck_assert_int_eq(1, file_name_in_file_list("bar", list)); ck_assert_int_eq(1, file_name_in_file_list("baz", list)); free_file_list(list); } END_TEST static Suite *file_list_suite(void) { Suite *s; TCase *tc_core; s = suite_create("File List"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_file_list_push_back); tcase_add_test(tc_core, test_make_policy_file); tcase_add_test(tc_core, test_file_name_in_file_list); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = file_list_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_if_checks.c000066400000000000000000000155501475050262500174110ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "test_utils.h" #include "../src/if_checks.h" #include "../src/check_hooks.h" #include "../src/maps.h" START_TEST (test_check_interface_defs_have_comment) { struct policy_node *head = calloc(1, sizeof(struct policy_node)); head->flavor = NODE_COMMENT; head->next = calloc(1, sizeof(struct policy_node)); head->next->prev = head; head->next->flavor = NODE_INTERFACE_DEF; struct check_result *res = check_interface_definitions_have_comment(NULL, head->next); ck_assert_ptr_null(res); head->next->flavor = NODE_TEMP_DEF; res = check_interface_definitions_have_comment(NULL, head->next); ck_assert_ptr_null(res); head->flavor = NODE_IF_FILE; res = check_interface_definitions_have_comment(NULL, head->next); ck_assert_ptr_nonnull(res); ck_assert_int_eq('C', res->severity); ck_assert_int_eq(C_ID_IF_COMMENT, res->check_id); free_check_result(res); head->next->flavor = NODE_AV_RULE; res = check_interface_definitions_have_comment(NULL, head->next); ck_assert_ptr_nonnull(res); ck_assert_int_eq('F', res->severity); ck_assert_int_eq(F_ID_INTERNAL, res->check_id); free_check_result(res); free_policy_node(head); } END_TEST START_TEST(test_check_type_used_but_not_required_in_if) { struct policy_node *head = calloc(1, sizeof(struct policy_node)); head->flavor = NODE_INTERFACE_DEF; struct policy_node *cur = head->first_child = calloc(1, sizeof(struct policy_node)); cur->flavor = NODE_GEN_REQ; cur->parent = head; cur->first_child = calloc(1, sizeof(struct policy_node)); cur->first_child->parent = cur; cur = cur->first_child; cur->flavor = NODE_START_BLOCK; cur->next = calloc(1, sizeof(struct policy_node)); cur->next->prev = cur; cur->next->parent = cur->parent; cur = cur->next; cur->flavor = NODE_DECL; struct declaration_data *data = calloc(1, sizeof(struct declaration_data)); cur->data.d_data = data; data->flavor = DECL_TYPE; data->name = strdup("bar_t"); cur = cur->parent; cur->next = calloc(1, sizeof(struct policy_node)); cur->next->prev = cur; cur->next->parent = cur->parent; cur = cur->next; cur->flavor = NODE_AV_RULE; cur->data.av_data = make_example_av_rule(); insert_into_decl_map("foo_t", "test", DECL_TYPE); insert_into_decl_map("bar_t", "test", DECL_TYPE); insert_into_decl_map("baz_t", "test", DECL_TYPE); struct av_rule_data *av_data = cur->data.av_data; free(av_data->sources->string); av_data->sources->string = strdup("$1"); const struct check_data cdata = { NULL, NULL, NULL, FILE_IF_FILE, NULL }; struct check_result *res = check_name_used_but_not_required_in_if(&cdata, cur); ck_assert_ptr_nonnull(res); ck_assert_int_eq(W_ID_NO_REQ, res->check_id); ck_assert_str_eq("Type baz_t is used in interface but not required", res->message); free_check_result(res); free_policy_node(head); free_all_maps(); } END_TEST START_TEST (test_check_type_required_but_not_used_in_if) { struct policy_node *head = calloc(1, sizeof(struct policy_node)); head->flavor = NODE_INTERFACE_DEF; head->data.str = strdup("interface_name"); struct policy_node *cur = head->first_child = calloc(1, sizeof(struct policy_node)); cur->flavor = NODE_GEN_REQ; cur->parent = head; cur->first_child = calloc(1, sizeof(struct policy_node)); cur->first_child->parent = cur; cur = cur->first_child; cur->flavor = NODE_START_BLOCK; cur->next = calloc(1, sizeof(struct policy_node)); cur->next->prev = cur; cur->next->parent = cur->parent; cur = cur->next; cur->flavor = NODE_DECL; struct declaration_data *data = calloc(1, sizeof(struct declaration_data)); cur->data.d_data = data; data->flavor = DECL_TYPE; data->name = strdup("bar_t"); cur = cur->parent; cur->next = calloc(1, sizeof(struct policy_node)); cur->next->prev = cur; cur->next->parent = cur->parent; cur = cur->next; cur->flavor = NODE_AV_RULE; cur->data.av_data = make_example_av_rule(); cur = cur->prev->first_child->next; // the declaration const struct check_data cdata = { NULL, NULL, NULL, FILE_IF_FILE, NULL }; ck_assert_ptr_null(check_name_required_but_not_used_in_if(&cdata, cur)); free(data->name); data->name = strdup("not_used_t"); struct check_result *res = check_name_required_but_not_used_in_if(&cdata, cur); ck_assert_ptr_nonnull(res); free_check_result(res); free_policy_node(head); } END_TEST START_TEST (test_system_r_exception) { insert_into_decl_map("system_r", "test", DECL_ROLE); struct policy_node *head = calloc(1, sizeof(struct policy_node)); head->flavor = NODE_INTERFACE_DEF; struct policy_node *cur = head->first_child = calloc(1, sizeof(struct policy_node)); cur->flavor = NODE_GEN_REQ; cur->parent = head; cur->first_child = calloc(1, sizeof(struct policy_node)); cur->first_child->parent = cur; cur = cur->first_child; cur->flavor = NODE_START_BLOCK; cur->next = calloc(1, sizeof(struct policy_node)); cur->next->prev = cur; cur->next->parent = cur->parent; cur = cur->next; cur->flavor = NODE_DECL; struct declaration_data *data = calloc(1, sizeof(struct declaration_data)); cur->data.d_data = data; data->flavor = DECL_ROLE; data->name = strdup("staff_r"); cur = cur->parent; cur->next = calloc(1, sizeof(struct policy_node)); cur->next->prev = cur; cur->next->parent = cur->parent; cur = cur->next; cur->flavor = NODE_ROLE_ALLOW; cur->data.ra_data = calloc(1, sizeof(struct role_allow_data)); cur->data.ra_data->from = sl_from_str("system_r"); cur->data.ra_data->to = sl_from_str("staff_r"); const struct check_data cdata = { NULL, NULL, NULL, FILE_IF_FILE, NULL }; ck_assert_ptr_null(check_name_used_but_not_required_in_if(&cdata, cur)); free_policy_node(head); free_all_maps(); } END_TEST static Suite *if_checks_suite(void) { Suite *s; TCase *tc_core; s = suite_create("IF_Checks"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_check_interface_defs_have_comment); tcase_add_test(tc_core, test_check_type_used_but_not_required_in_if); tcase_add_test(tc_core, test_check_type_required_but_not_used_in_if); tcase_add_test(tc_core, test_system_r_exception); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = if_checks_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_maps.c000066400000000000000000000166031475050262500164330ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "../src/maps.h" START_TEST (test_insert_into_type_map) { insert_into_decl_map("foo_t", "test_module", DECL_TYPE); insert_into_decl_map("bar_t", "test_module", DECL_TYPE); insert_into_decl_map("baz_t", "other_module", DECL_TYPE); const char *mod_name = look_up_in_decl_map("doesntexist", DECL_TYPE); ck_assert_ptr_null(mod_name); mod_name = look_up_in_decl_map("foo_t", DECL_TYPE); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq(mod_name, "test_module"); mod_name = look_up_in_decl_map("bar_t", DECL_TYPE); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq(mod_name, "test_module"); mod_name = look_up_in_decl_map("baz_t", DECL_TYPE); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq(mod_name, "other_module"); ck_assert_int_eq(decl_map_count(DECL_TYPE), 3); free_all_maps(); } END_TEST START_TEST (test_insert_into_type_map_dup) { insert_into_decl_map("foo_t", "test_module", DECL_TYPE); insert_into_decl_map("foo_t", "other_module", DECL_TYPE); ck_assert_int_eq(decl_map_count(DECL_TYPE), 1); free_all_maps(); } END_TEST START_TEST (test_role_and_user_maps) { insert_into_decl_map("foo_r", "test_module1", DECL_ROLE); insert_into_decl_map("bar_r", "test_module2", DECL_ROLE); insert_into_decl_map("bar_u", "test_module3", DECL_USER); const char *mod_name = look_up_in_decl_map("foo_r", DECL_TYPE); ck_assert_ptr_null(mod_name); mod_name = look_up_in_decl_map("foo_r", DECL_ROLE); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq(mod_name, "test_module1"); mod_name = look_up_in_decl_map("foo_r", DECL_ATTRIBUTE); ck_assert_ptr_null(mod_name); mod_name = look_up_in_decl_map("bar_u", DECL_ROLE); ck_assert_ptr_null(mod_name); mod_name = look_up_in_decl_map("bar_u", DECL_USER); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq(mod_name, "test_module3"); ck_assert_int_eq(decl_map_count(DECL_TYPE), 0); ck_assert_int_eq(decl_map_count(DECL_ROLE), 2); ck_assert_int_eq(decl_map_count(DECL_USER), 1); free_all_maps(); } END_TEST START_TEST (test_class_and_perm_maps) { insert_into_decl_map("file", "class", DECL_CLASS); insert_into_decl_map("read", "perm", DECL_PERM); const char *res = look_up_in_decl_map("dir", DECL_CLASS); ck_assert_ptr_null(res); res = look_up_in_decl_map("file", DECL_CLASS); ck_assert_ptr_nonnull(res); res = look_up_in_decl_map("read", DECL_PERM); ck_assert_ptr_nonnull(res); ck_assert_int_eq(decl_map_count(DECL_CLASS), 1); ck_assert_int_eq(decl_map_count(DECL_PERM), 1); free_all_maps(); } END_TEST START_TEST (test_mods_map) { insert_into_mods_map("systemd", "base"); insert_into_mods_map("games", "off"); const char *res = look_up_in_mods_map("systemd"); ck_assert_str_eq("base", res); res = look_up_in_mods_map("games"); ck_assert_str_eq("off", res); res = look_up_in_mods_map("foo"); ck_assert_ptr_null(res); free_all_maps(); } END_TEST START_TEST (test_insert_decl_into_template_map) { insert_decl_into_template_map("user_domain", DECL_TYPE, "$1_t"); insert_decl_into_template_map("user_domain", DECL_TYPE, "$1_exec_t"); insert_decl_into_template_map("user_domain", DECL_ROLE, "$1_r"); insert_decl_into_template_map("other_template", DECL_TYPE, "$1_conf_t"); const struct decl_list *dl = look_up_decl_in_template_map("doesntexist"); ck_assert_ptr_null(dl); dl = look_up_decl_in_template_map("user_domain"); ck_assert_ptr_nonnull(dl); ck_assert_ptr_nonnull(dl->decl); ck_assert_int_eq(dl->decl->flavor, DECL_TYPE); ck_assert_ptr_nonnull(dl->decl->name); ck_assert_str_eq(dl->decl->name, "$1_t"); ck_assert_ptr_null(dl->decl->attrs); dl = dl->next; ck_assert_ptr_nonnull(dl); ck_assert_ptr_nonnull(dl->decl); ck_assert_int_eq(dl->decl->flavor, DECL_TYPE); ck_assert_ptr_nonnull(dl->decl->name); ck_assert_str_eq(dl->decl->name, "$1_exec_t"); ck_assert_ptr_null(dl->decl->attrs); dl = dl->next; ck_assert_ptr_nonnull(dl); ck_assert_ptr_nonnull(dl->decl); ck_assert_int_eq(dl->decl->flavor, DECL_ROLE); ck_assert_ptr_nonnull(dl->decl->name); ck_assert_str_eq(dl->decl->name, "$1_r"); ck_assert_ptr_null(dl->decl->attrs); ck_assert_ptr_null(dl->next); dl = look_up_decl_in_template_map("other_template"); ck_assert_ptr_nonnull(dl); ck_assert_ptr_nonnull(dl->decl); ck_assert_int_eq(dl->decl->flavor, DECL_TYPE); ck_assert_ptr_nonnull(dl->decl->name); ck_assert_str_eq(dl->decl->name, "$1_conf_t"); ck_assert_ptr_null(dl->decl->attrs); free_all_maps(); } END_TEST START_TEST (test_insert_call_into_template_map) { struct if_call_data *call = malloc(sizeof(struct if_call_data)); call->name = strdup("foo"); call->args = calloc(1, sizeof(struct string_list)); call->args->string = strdup("bar_t"); call->args->next = NULL; insert_call_into_template_map("user_domain", call); insert_decl_into_template_map("user_domain", DECL_TYPE, "$1_conf_t"); const struct if_call_list *out = look_up_call_in_template_map("user_domain"); ck_assert_ptr_eq(call, out->call); free_if_call_data(call); } END_TEST static size_t test_permmacro_map_count = 0; static void test_permmacro_map_visitor(const char *key, const struct string_list *val) { test_permmacro_map_count += strlen(key); test_permmacro_map_count += strlen(val->string); } START_TEST (test_permmacro_map) { struct string_list *sl1 = sl_from_str("test"); ck_assert_ptr_nonnull(sl1); struct string_list *sl2 = sl_from_strs(3, "hello", "world", "!"); ck_assert_ptr_nonnull(sl2); insert_into_permmacros_map("test", sl1); // consumes sl1 insert_into_permmacros_map("standard", sl2); // consumes sl2 ck_assert_ptr_null(look_up_in_permmacros_map("hello")); ck_assert_ptr_null(look_up_in_permmacros_map("Test")); const struct string_list *csl = look_up_in_permmacros_map("standard"); ck_assert_ptr_nonnull(csl); ck_assert_str_eq("hello", csl->string); ck_assert_ptr_nonnull(csl->next); ck_assert_str_eq("world", csl->next->string); visit_all_in_permmacros_map(test_permmacro_map_visitor); ck_assert_uint_eq(2 * strlen("test") + strlen("standard") + strlen("hello"), test_permmacro_map_count); free_all_maps(); } END_TEST static Suite *maps_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Maps"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_insert_into_type_map); tcase_add_test(tc_core, test_insert_into_type_map_dup); tcase_add_test(tc_core, test_role_and_user_maps); tcase_add_test(tc_core, test_class_and_perm_maps); tcase_add_test(tc_core, test_mods_map); tcase_add_test(tc_core, test_insert_decl_into_template_map); tcase_add_test(tc_core, test_insert_call_into_template_map); tcase_add_test(tc_core, test_permmacro_map); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = maps_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_name_list.c000066400000000000000000000205761475050262500174520ustar00rootroot00000000000000/* * Copyright 2022 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../src/name_list.h" #include "../src/tree.h" START_TEST (test_name_list_create) { struct name_list *nl = name_list_create("foo", NAME_ROLEATTRIBUTE); ck_assert_ptr_nonnull(nl); ck_assert_ptr_nonnull(nl->data); ck_assert_str_eq("foo", nl->data->name); ck_assert_int_eq(NAME_ROLEATTRIBUTE, nl->data->flavor); ck_assert_ptr_null(nl->data->traits); ck_assert_ptr_null(nl->next); free_name_list(nl); } END_TEST START_TEST (test_name_list_from_sl) { struct string_list *sl, *traits; struct name_list *nl; sl = NULL; nl = name_list_from_sl_with_traits(sl, NAME_TYPE, NULL); ck_assert_ptr_null(nl); sl = sl_from_strs(2, "foo", "bar"); nl = name_list_from_sl_with_traits(sl, NAME_TYPE, NULL); ck_assert_ptr_nonnull(nl); ck_assert_ptr_nonnull(nl->data); ck_assert_str_eq("foo", nl->data->name); ck_assert_int_eq(NAME_TYPE, nl->data->flavor); ck_assert_ptr_null(nl->data->traits); ck_assert_ptr_nonnull(nl->next); ck_assert_str_eq("bar", nl->next->data->name); ck_assert_int_eq(NAME_TYPE, nl->next->data->flavor); ck_assert_ptr_null(nl->next->data->traits); ck_assert_ptr_null(nl->next->next); free_name_list(nl); free_string_list(sl); sl = sl_from_strs(3, "foo", "bar", "baz"); traits = sl_from_strs(2, "alpha", "beta"); nl = name_list_from_sl_with_traits(sl, NAME_ROLE, traits); ck_assert_ptr_nonnull(nl); ck_assert_ptr_nonnull(nl->data); ck_assert_str_eq("foo", nl->data->name); ck_assert_int_eq(NAME_ROLE, nl->data->flavor); ck_assert_ptr_nonnull(nl->data->traits); ck_assert_str_eq("alpha", nl->data->traits->string); ck_assert_str_eq("beta", nl->data->traits->next->string); ck_assert_ptr_null(nl->data->traits->next->next); ck_assert_ptr_nonnull(nl->next); ck_assert_ptr_nonnull(nl->next->data); ck_assert_str_eq("bar", nl->next->data->name); ck_assert_int_eq(NAME_ROLE, nl->next->data->flavor); ck_assert_ptr_nonnull(nl->next->data->traits); ck_assert_str_eq("alpha", nl->next->data->traits->string); ck_assert_str_eq("beta", nl->next->data->traits->next->string); ck_assert_ptr_null(nl->next->data->traits->next->next); ck_assert_ptr_nonnull(nl->next->next); ck_assert_ptr_nonnull(nl->next->next->data); ck_assert_str_eq("baz", nl->next->next->data->name); ck_assert_int_eq(NAME_ROLE, nl->next->next->data->flavor); ck_assert_ptr_nonnull(nl->next->next->data->traits); ck_assert_str_eq("alpha", nl->next->next->data->traits->string); ck_assert_str_eq("beta", nl->next->next->data->traits->next->string); ck_assert_ptr_null(nl->next->next->data->traits->next->next); ck_assert_ptr_null(nl->next->next->next); free_name_list(nl); free_string_list(traits); free_string_list(sl); } END_TEST START_TEST (test_concat_name_lists) { struct name_list *res, *nl1, *nl2; ck_assert_ptr_null(concat_name_lists(NULL, NULL)); nl1 = name_list_create("hello", NAME_TYPEATTRIBUTE); ck_assert_ptr_nonnull(nl1); nl2 = name_list_create("world", NAME_CLASS); ck_assert_ptr_nonnull(nl2); res = concat_name_lists(NULL, nl1); ck_assert_ptr_nonnull(res); ck_assert_str_eq("hello", res->data->name); ck_assert_int_eq(NAME_TYPEATTRIBUTE, res->data->flavor); ck_assert_ptr_null(res->next); res = concat_name_lists(nl1, NULL); ck_assert_ptr_nonnull(res); ck_assert_str_eq("hello", res->data->name); ck_assert_int_eq(NAME_TYPEATTRIBUTE, res->data->flavor); ck_assert_ptr_null(res->next); res = concat_name_lists(nl1, nl2); ck_assert_ptr_nonnull(res); ck_assert_str_eq("hello", res->data->name); ck_assert_int_eq(NAME_TYPEATTRIBUTE, res->data->flavor); ck_assert_ptr_nonnull(res->next); ck_assert_str_eq("world", res->next->data->name); ck_assert_int_eq(NAME_CLASS, res->next->data->flavor); ck_assert_ptr_null(res->next->next); free_name_list(res); // frees nl1 and nl2 } END_TEST START_TEST (test_name_lists_from_type_decl) { struct declaration_data *decl = malloc(sizeof(struct declaration_data)); decl->flavor = DECL_TYPE; decl->name = strdup("foo"); decl->attrs = sl_from_strs(2, "alpha", "beta"); struct name_list *nl = name_list_from_decl(decl); free_string_list(decl->attrs); free(decl->name); free(decl); ck_assert_ptr_nonnull(nl); ck_assert_ptr_nonnull(nl->data); ck_assert_str_eq("foo", nl->data->name); ck_assert_int_eq(NAME_TYPE, nl->data->flavor); ck_assert_ptr_null(nl->data->traits); ck_assert_ptr_nonnull(nl->next); ck_assert_ptr_nonnull(nl->next->data); ck_assert_str_eq("alpha", nl->next->data->name); ck_assert_int_eq(NAME_TYPEATTRIBUTE, nl->next->data->flavor); ck_assert_ptr_null(nl->next->data->traits); ck_assert_ptr_nonnull(nl->next->next); ck_assert_ptr_nonnull(nl->next->next->data); ck_assert_str_eq("beta", nl->next->next->data->name); ck_assert_int_eq(NAME_TYPEATTRIBUTE, nl->next->next->data->flavor); ck_assert_ptr_null(nl->next->next->data->traits); ck_assert_ptr_null(nl->next->next->next); free_name_list(nl); } END_TEST START_TEST (test_name_lists_from_class_decl) { struct declaration_data *decl = malloc(sizeof(struct declaration_data)); decl->flavor = DECL_CLASS; decl->name = strdup("foo"); decl->attrs = sl_from_strs(2, "alpha", "beta"); struct name_list *nl = name_list_from_decl(decl); free_string_list(decl->attrs); free(decl->name); free(decl); ck_assert_ptr_nonnull(nl); ck_assert_ptr_nonnull(nl->data); ck_assert_str_eq("foo", nl->data->name); ck_assert_int_eq(NAME_CLASS, nl->data->flavor); ck_assert_ptr_nonnull(nl->data->traits); ck_assert_str_eq("alpha", nl->data->traits->string); ck_assert_str_eq("beta", nl->data->traits->next->string); ck_assert_ptr_null(nl->data->traits->next->next); ck_assert_ptr_null(nl->next); free_name_list(nl); } END_TEST START_TEST (test_name_list_contains) { struct name_list *d; struct name_list *nl = concat_name_lists( name_list_create("foo", NAME_TYPEATTRIBUTE), name_list_create("bar", NAME_ROLE)); ck_assert_ptr_nonnull(nl); d = name_list_create("foo", NAME_TYPEATTRIBUTE); ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("foo", NAME_TYPE_OR_ATTRIBUTE); ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("foo", NAME_UNKNOWN); ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("foo", NAME_TYPE); ck_assert_int_eq(0, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("foo", NAME_BOOL); ck_assert_int_eq(0, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("bar", NAME_ROLE); ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("bar", NAME_ROLE_OR_ATTRIBUTE); ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("bar", NAME_UNKNOWN); ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("bar", NAME_ROLEATTRIBUTE); ck_assert_int_eq(0, name_list_contains_name(nl, d->data)); free_name_list(d); d = name_list_create("bar", NAME_TYPE); ck_assert_int_eq(0, name_list_contains_name(nl, d->data)); free_name_list(d); free_name_list(nl); } END_TEST static Suite *name_list_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Name_list"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_name_list_create); tcase_add_test(tc_core, test_name_list_from_sl); tcase_add_test(tc_core, test_concat_name_lists); tcase_add_test(tc_core, test_name_lists_from_type_decl); tcase_add_test(tc_core, test_name_lists_from_class_decl); tcase_add_test(tc_core, test_name_list_contains); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = name_list_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_ordering.c000066400000000000000000000420211475050262500172750ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../src/runner.h" #include "../src/ordering.h" #include "../src/maps.h" static enum order_difference_reason always_greater(__attribute__((unused)) const struct ordering_metadata *order_data, __attribute__((unused)) const struct policy_node *first, __attribute__((unused)) const struct policy_node *second) { return 1; } static enum order_difference_reason always_less(__attribute__((unused)) const struct ordering_metadata *order_data, __attribute__((unused)) const struct policy_node *first, __attribute__((unused)) const struct policy_node *second) { return -1; } START_TEST (test_prepare_ordering_metadata) { struct policy_node *head = calloc(1, sizeof(struct policy_node)); head->next = calloc(1, sizeof(struct policy_node)); head->flavor = NODE_TE_FILE; struct policy_node *cur = head->next; cur->flavor = NODE_DECL; cur->next = calloc(1, sizeof(struct policy_node)); cur = cur->next; cur->flavor = NODE_DECL; cur->next = calloc(1, sizeof(struct policy_node)); cur->next->flavor = NODE_DECL; struct check_data data; data.mod_name = strdup("foo"); struct ordering_metadata *o = prepare_ordering_metadata(&data, head); ck_assert_ptr_nonnull(o); ck_assert_ptr_nonnull(o->sections); ck_assert_ptr_nonnull(o->sections->section_name); ck_assert_uint_eq(o->order_node_len, 3); ck_assert_ptr_null(o->nodes[0].node); ck_assert_ptr_null(o->nodes[1].node); ck_assert_ptr_null(o->nodes[2].node); free(data.mod_name); free_ordering_metadata(o); free_policy_node(head); } END_TEST #define POLICIES_DIR SAMPLE_POL_DIR #define UNCOMMON_TE_FILENAME POLICIES_DIR "uncommon.te" START_TEST (test_ordering_uncommon_policy) { struct policy_node *head = parse_one_file(UNCOMMON_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(head); struct check_data data; data.mod_name = strdup("foo"); struct ordering_metadata *o = prepare_ordering_metadata(&data, head); ck_assert_ptr_nonnull(o); calculate_longest_increasing_subsequence(head, o, always_greater); ck_assert_ptr_eq(o->nodes[0].node, head->next); ck_assert_int_eq(o->nodes[0].in_order, 1); free(data.mod_name); free_ordering_metadata(o); free_policy_node(head); cleanup_parsing(); } END_TEST START_TEST (test_calculate_longest_increasing_subsequence) { struct policy_node *head = calloc(1, sizeof(struct policy_node)); head->flavor = NODE_TE_FILE; head->next = calloc(1, sizeof(struct policy_node)); struct policy_node *cur = head->next; cur->flavor = NODE_DECL; cur->next = calloc(1, sizeof(struct policy_node)); cur = cur->next; cur->flavor = NODE_DECL; cur->next = calloc(1, sizeof(struct policy_node)); cur->next->flavor = NODE_DECL; struct check_data data; data.mod_name = strdup("foo"); struct ordering_metadata *o = prepare_ordering_metadata(&data, head); ck_assert_ptr_nonnull(o); calculate_longest_increasing_subsequence(head, o, always_greater); ck_assert_ptr_eq(o->nodes[0].node, head->next); ck_assert_int_eq(o->nodes[0].in_order, 1); free_ordering_metadata(o); o = prepare_ordering_metadata(&data, head); calculate_longest_increasing_subsequence(head, o, always_less); ck_assert_ptr_eq(o->nodes[0].node, head->next); ck_assert_int_eq(o->nodes[0].in_order, 0); free(data.mod_name); free_ordering_metadata(o); free_policy_node(head); } END_TEST START_TEST (test_add_section_info) { struct section_data *sections = calloc(1, sizeof(struct section_data)); add_section_info(sections, "foo", 2); ck_assert_str_eq(sections->section_name, "foo"); ck_assert_int_eq(sections->lines_sum, 2); ck_assert_int_eq(sections->lineno_count, 1); ck_assert_ptr_null(sections->next); add_section_info(sections, "foo", 4); ck_assert_str_eq(sections->section_name, "foo"); ck_assert_int_eq(sections->lines_sum, 6); ck_assert_int_eq(sections->lineno_count, 2); ck_assert_ptr_null(sections->next); add_section_info(sections, "bar", 5); ck_assert_str_eq(sections->section_name, "foo"); ck_assert_int_eq(sections->lines_sum, 6); ck_assert_int_eq(sections->lineno_count, 2); ck_assert_ptr_nonnull(sections->next); ck_assert_str_eq(sections->next->section_name, "bar"); ck_assert_int_eq(sections->next->lines_sum, 5); ck_assert_int_eq(sections->next->lineno_count, 1); ck_assert_ptr_null(sections->next->next); free_section_data(sections); } END_TEST START_TEST (test_get_section) { ck_assert_ptr_null(get_section(NULL)); struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_TE_FILE; ck_assert_ptr_null(get_section(node)); node->flavor = NODE_IF_FILE; ck_assert_ptr_null(get_section(node)); node->flavor = NODE_FC_FILE; ck_assert_ptr_null(get_section(node)); node->flavor = NODE_AV_RULE; node->data.av_data = calloc(1, sizeof(struct av_rule_data)); node->data.av_data->sources = calloc(1, sizeof(struct string_list)); node->data.av_data->sources->string = strdup("foo_t"); ck_assert_str_eq("foo_t", get_section(node)); free_av_rule_data(node->data.av_data); node->data.av_data = NULL; node->flavor = NODE_ROLE_ALLOW; ck_assert_str_eq("_non_ordered", get_section(node)); node->flavor = NODE_DECL; ck_assert_str_eq("_declaration", get_section(node)); node->flavor = NODE_ALIAS; ck_assert_str_eq("_declaration", get_section(node)); node->flavor = NODE_TYPE_ALIAS; ck_assert_str_eq("_declaration", get_section(node)); node->flavor = NODE_TYPE_ATTRIBUTE; ck_assert_str_eq("_declaration", get_section(node)); node->flavor = NODE_OPTIONAL_POLICY; node->first_child = calloc(1, sizeof(struct policy_node)); node->first_child->flavor = NODE_START_BLOCK; node->first_child->next = calloc(1, sizeof(struct policy_node)); node->first_child->next->flavor = NODE_IF_CALL; node->first_child->next->data.ic_data = calloc(1, sizeof(struct if_call_data)); node->first_child->next->data.ic_data->name = strdup("foo_read"); node->first_child->next->data.ic_data->args = calloc(1, sizeof(struct string_list)); node->first_child->next->data.ic_data->args->string = strdup("bar_t"); ck_assert_str_eq("bar_t", get_section(node)); node->flavor = NODE_REQUIRE; ck_assert_str_eq("_non_ordered", get_section(node)); free_policy_node(node); } END_TEST START_TEST (test_calculate_average_lines) { // Make sure no segfault on NULL. No return to check. calculate_average_lines(NULL); struct section_data *sections = calloc(1, sizeof(struct section_data)); sections->section_name = strdup("foo"); sections->lineno_count = 4; sections->lines_sum = 21; sections->next = calloc(1, sizeof(struct section_data)); sections->next->lineno_count = 10; sections->next->lines_sum = 40; calculate_average_lines(sections); ck_assert_float_eq_tol((float) 5.25, sections->avg_line, (float) 0.001); ck_assert_float_eq_tol((float) 4, sections->next->avg_line, (float) 0.001); free_section_data(sections); } END_TEST START_TEST (test_get_local_subsection) { ck_assert_int_eq(LSS_UNKNOWN, get_local_subsection("foo", NULL, ORDER_REF)); struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_AV_RULE; node->data.av_data = calloc(1, sizeof(struct av_rule_data)); node->data.av_data->targets = calloc(1, sizeof(struct string_list)); node->data.av_data->targets->string = strdup("self"); ck_assert_int_eq(LSS_SELF, get_local_subsection("foo", node, ORDER_REF)); free(node->data.av_data->targets->string); node->data.av_data->sources = calloc(1, sizeof(struct string_list)); node->data.av_data->sources->string = strdup("foo_t"); node->data.av_data->targets->string = strdup("foo_log_t"); insert_into_decl_map("foo_t", "foo", DECL_TYPE); insert_into_decl_map("foo_log_t", "foo", DECL_TYPE); insert_into_decl_map("foo_config", "foo", DECL_ATTRIBUTE); ck_assert_int_eq(LSS_OWN, get_local_subsection("foo", node, ORDER_REF)); free(node->data.av_data->targets->string); node->data.av_data->targets->string = strdup("foo_config"); ck_assert_int_eq(LSS_OWN, get_local_subsection("foo", node, ORDER_REF)); free(node->data.av_data->targets->string); node->data.av_data->targets->string = strdup("bar_data_t"); insert_into_decl_map("bar_data_t", "bar", DECL_TYPE); // raw allow to other module. Not mentioned in style guide ck_assert_int_eq(LSS_UNKNOWN, get_local_subsection("foo", node, ORDER_REF)); free_all_maps(); free_policy_node(node); } END_TEST START_TEST (test_get_local_subsection_related_if) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_IF_CALL; node->data.ic_data = malloc(sizeof(struct if_call_data)); node->data.ic_data->name = strdup("foo_if"); node->data.ic_data->args = sl_from_str("foo_t"); insert_into_ifs_map("foo_if", "foo"); insert_into_ifs_map("foo_sub_if", "foo_sub"); ck_assert_int_eq(LSS_OWN, get_local_subsection("foo", node, ORDER_LIGHT)); ck_assert_int_eq(LSS_RELATED, get_local_subsection("foo_sub", node, ORDER_LIGHT)); ck_assert_int_eq(LSS_OTHER, get_local_subsection("foo_sub", node, ORDER_REF)); free(node->data.ic_data->name); node->data.ic_data->name = strdup("foo_sub_if"); ck_assert_int_eq(LSS_OWN, get_local_subsection("foo_sub", node, ORDER_LIGHT)); ck_assert_int_eq(LSS_RELATED, get_local_subsection("foo", node, ORDER_LIGHT)); ck_assert_int_eq(LSS_OTHER, get_local_subsection("foo", node, ORDER_REF)); free_policy_node(node); free_all_maps(); } END_TEST START_TEST (test_compare_nodes_refpolicy) { struct policy_node *head = calloc(1, sizeof(struct policy_node)); struct policy_node *first = calloc(1, sizeof(struct policy_node)); struct policy_node *second = calloc(1, sizeof(struct policy_node)); head->next = first; first->next = second; first->flavor = NODE_DECL; second->flavor = NODE_AV_RULE; second->data.av_data = calloc(1, sizeof(struct av_rule_data)); second->data.av_data->sources = calloc(1, sizeof(struct string_list)); second->data.av_data->sources->string = strdup("foo_t"); struct check_data data; data.mod_name = strdup("foo"); struct ordering_metadata *o = prepare_ordering_metadata(&data, head); ck_assert_int_eq(ORDER_SECTION, compare_nodes_refpolicy(o, first, second)); ck_assert_int_eq(-ORDER_SECTION, compare_nodes_refpolicy(o, second, first)); free_av_rule_data(second->data.av_data); second->data.av_data = NULL; second->flavor = NODE_DECL; first->data.d_data = calloc(1, sizeof(struct declaration_data)); second->data.d_data = calloc(1, sizeof(struct declaration_data)); first->data.d_data->flavor = DECL_BOOL; second->data.d_data->flavor = DECL_ATTRIBUTE; ck_assert_int_eq(ORDER_DECLARATION_SUBSECTION, compare_nodes_refpolicy(o, first, second)); first->data.d_data->flavor = DECL_TYPE; ck_assert_int_eq(-ORDER_DECLARATION_SUBSECTION, compare_nodes_refpolicy(o, first, second)); free(data.mod_name); free_ordering_metadata(o); free_policy_node(head); } END_TEST START_TEST (test_alphabetical_if_calls) { struct policy_node *head = calloc(1, sizeof(struct policy_node)); struct policy_node *first = calloc(1, sizeof(struct policy_node)); struct policy_node *second = calloc(1, sizeof(struct policy_node)); struct policy_node *third = calloc(1, sizeof(struct policy_node)); head->next = first; first->next = second; second->next = third; first->flavor = NODE_IF_CALL; second->flavor = NODE_IF_CALL; third->flavor = NODE_IF_CALL; first->data.ic_data = malloc(sizeof(struct if_call_data)); first->data.ic_data->name = strdup("moduleA_if1"); first->data.ic_data->args = sl_from_str("foo_t"); second->data.ic_data = malloc(sizeof(struct if_call_data)); second->data.ic_data->name = strdup("moduleA_if2"); second->data.ic_data->args = sl_from_str("foo_t"); third->data.ic_data = malloc(sizeof(struct if_call_data)); third->data.ic_data->name = strdup("moduleB_if"); third->data.ic_data->args = sl_from_str("foo_t"); struct check_data data; data.mod_name = strdup("foo"); insert_into_ifs_map("moduleA_if1", "moduleA"); insert_into_ifs_map("moduleA_if2", "moduleA"); insert_into_ifs_map("moduleB_if", "moduleB"); struct ordering_metadata *o = prepare_ordering_metadata(&data, head); ck_assert_int_eq(ORDER_EQUAL, compare_nodes_refpolicy(o, first, second)); ck_assert_int_eq(ORDER_EQUAL, compare_nodes_refpolicy(o, second, first)); ck_assert_int_eq(ORDER_ALPHABETICAL, compare_nodes_refpolicy(o, second, third)); ck_assert_int_eq(-ORDER_ALPHABETICAL, compare_nodes_refpolicy(o, third, second)); free(data.mod_name); free_ordering_metadata(o); free_policy_node(head); } END_TEST START_TEST (test_alphabetical_optionals) { // Setup struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); cur->flavor = NODE_TE_FILE; struct policy_node *head = cur; ck_assert_int_eq(SELINT_SUCCESS, begin_optional_policy(&cur, 1)); ck_assert_int_eq(SELINT_SUCCESS, insert_interface_call(&cur, "moduleA_if", sl_from_str("foo_t"), 2)); ck_assert_int_eq(SELINT_SUCCESS, insert_interface_call(&cur, "moduleD_if", sl_from_str("foo_t"), 2)); ck_assert_int_eq(SELINT_SUCCESS, end_optional_policy(&cur)); ck_assert_int_eq(SELINT_SUCCESS, begin_optional_policy(&cur, 4)); ck_assert_int_eq(SELINT_SUCCESS, insert_interface_call(&cur, "moduleC_if", sl_from_str("foo_t"), 5)); ck_assert_int_eq(SELINT_SUCCESS, insert_interface_call(&cur, "moduleB_if", sl_from_str("foo_t"), 5)); ck_assert_int_eq(SELINT_SUCCESS, end_optional_policy(&cur)); insert_into_ifs_map("moduleA_if", "moduleA"); insert_into_ifs_map("moduleB_if", "moduleB"); insert_into_ifs_map("moduleC_if", "moduleC"); insert_into_ifs_map("moduleD_if", "moduleD"); const struct policy_node *A_call = head->next->first_child->next; const struct policy_node *B_call = head->next->next->first_child->next->next; const struct policy_node *C_call = head->next->next->first_child->next; const struct policy_node *D_call = head->next->first_child->next->next; ck_assert_int_eq(NODE_OPTIONAL_POLICY, head->next->flavor); ck_assert_int_eq(NODE_START_BLOCK, head->next->first_child->flavor); ck_assert_int_eq(NODE_OPTIONAL_POLICY, head->next->next->flavor); ck_assert_int_eq(NODE_START_BLOCK, head->next->next->first_child->flavor); ck_assert_int_eq(NODE_IF_CALL, A_call->flavor); ck_assert_str_eq("moduleA_if", A_call->data.ic_data->name); ck_assert_int_eq(NODE_IF_CALL, B_call->flavor); ck_assert_str_eq("moduleB_if", B_call->data.ic_data->name); ck_assert_int_eq(NODE_IF_CALL, C_call->flavor); ck_assert_str_eq("moduleC_if", C_call->data.ic_data->name); ck_assert_int_eq(NODE_IF_CALL, D_call->flavor); ck_assert_str_eq("moduleD_if", D_call->data.ic_data->name); struct check_data data; data.mod_name = strdup("foo"); struct ordering_metadata *o = prepare_ordering_metadata(&data, head); // Actual tests // sort distinct optional blocks ck_assert_int_eq(ORDER_ALPHABETICAL, compare_nodes_refpolicy(o, head->next, head->next->next)); ck_assert_int_eq(-ORDER_ALPHABETICAL, compare_nodes_refpolicy(o, head->next->next, head->next)); // do not sort inside optional block ck_assert_int_eq(ORDER_EQUAL, compare_nodes_refpolicy(o, C_call, B_call)); ck_assert_int_eq(ORDER_EQUAL, compare_nodes_refpolicy(o, B_call, C_call)); ck_assert_int_eq(ORDER_ALPHABETICAL, compare_nodes_refpolicy(o, A_call, C_call)); ck_assert_int_eq(-ORDER_ALPHABETICAL, compare_nodes_refpolicy(o, C_call, A_call)); // do not sort on non-first nodes ck_assert_int_eq(ORDER_EQUAL, compare_nodes_refpolicy(o, D_call, C_call)); ck_assert_int_eq(ORDER_EQUAL, compare_nodes_refpolicy(o, C_call, D_call)); // Cleanup free(data.mod_name); free_ordering_metadata(o); free_policy_node(head); cleanup_parsing(); } END_TEST static Suite *ordering_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Ordering"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_prepare_ordering_metadata); tcase_add_test(tc_core, test_ordering_uncommon_policy); tcase_add_test(tc_core, test_calculate_longest_increasing_subsequence); tcase_add_test(tc_core, test_add_section_info); tcase_add_test(tc_core, test_get_section); tcase_add_test(tc_core, test_calculate_average_lines); tcase_add_test(tc_core, test_get_local_subsection); tcase_add_test(tc_core, test_get_local_subsection_related_if); tcase_add_test(tc_core, test_compare_nodes_refpolicy); tcase_add_test(tc_core, test_alphabetical_if_calls); tcase_add_test(tc_core, test_alphabetical_optionals); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = ordering_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_parse_fc.c000066400000000000000000000166321475050262500172570ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "../src/tree.h" #include "../src/parse_fc.h" #define POLICIES_DIR SAMPLE_POL_DIR #define BASIC_FC_FILENAME POLICIES_DIR "basic.fc" #define WITH_M4_FILENAME POLICIES_DIR "with_m4.fc" #define NONE_CONTEXT_FILENAME POLICIES_DIR "none_context.fc" START_TEST (test_parse_context) { char context_str[] = "staff_u:staff_r:foo_t"; struct sel_context *ctx = parse_context(context_str); ck_assert_ptr_nonnull(ctx); ck_assert_str_eq("staff_u", ctx->user); ck_assert_str_eq("staff_r", ctx->role); ck_assert_str_eq("foo_t", ctx->type); ck_assert_ptr_null(ctx->range); ck_assert_int_eq(0, ctx->has_gen_context); free_sel_context(ctx); } END_TEST START_TEST (test_parse_context_missing_field) { char context_str[] = "staff_u:foo_t"; struct sel_context *ctx = parse_context(context_str); ck_assert_ptr_null(ctx); } END_TEST START_TEST (test_parse_fc_empty) { char line[] = ""; struct fc_entry *out = parse_fc_line(line); ck_assert_ptr_null(out); char line2[] = " "; out = parse_fc_line(line2); ck_assert_ptr_null(out); } END_TEST START_TEST (test_parse_fc_line_with_gen_context) { char line[] = "/usr/bin(/.*)? gen_context(system_u:object_r:bin_t, s0)"; struct fc_entry *out= parse_fc_line(line); ck_assert_ptr_nonnull(out); ck_assert_str_eq("/usr/bin(/.*)?", out->path); ck_assert(out->obj == '\0'); ck_assert_ptr_nonnull(out->context); ck_assert_int_eq(1, out->context->has_gen_context); ck_assert_str_eq("system_u", out->context->user); ck_assert_str_eq("object_r", out->context->role); ck_assert_str_eq("bin_t", out->context->type); ck_assert_str_eq("s0", out->context->range); free_fc_entry(out); char line2[] = "/usr/bin(/.*)? gen_context(system_u:object_r:bin_t)"; out= parse_fc_line(line2); ck_assert_ptr_nonnull(out); ck_assert_str_eq("/usr/bin(/.*)?", out->path); ck_assert(out->obj == '\0'); ck_assert_ptr_nonnull(out->context); ck_assert_int_eq(1, out->context->has_gen_context); ck_assert_str_eq("system_u", out->context->user); ck_assert_str_eq("object_r", out->context->role); ck_assert_str_eq("bin_t", out->context->type); ck_assert_ptr_null(out->context->range); free_fc_entry(out); } END_TEST START_TEST (test_parse_fc_line) { char line[] = "/usr/bin(/.*)? system_u:object_r:bin_t:s0"; struct fc_entry *out= parse_fc_line(line); ck_assert_ptr_nonnull(out); ck_assert_str_eq("/usr/bin(/.*)?", out->path); ck_assert(out->obj == '\0'); ck_assert_ptr_nonnull(out->context); ck_assert_int_eq(0, out->context->has_gen_context); ck_assert_str_eq("system_u", out->context->user); ck_assert_str_eq("object_r", out->context->role); ck_assert_str_eq("bin_t", out->context->type); ck_assert_str_eq("s0", out->context->range); free_fc_entry(out); } END_TEST START_TEST (test_parse_fc_line_with_obj) { char line[] = "/usr/bin(/.*)? -d system_u:object_r:bin_t:s0"; struct fc_entry *out= parse_fc_line(line); ck_assert_ptr_nonnull(out); ck_assert_str_eq("/usr/bin(/.*)?", out->path); ck_assert(out->obj == 'd'); ck_assert_ptr_nonnull(out->context); ck_assert_int_eq(0, out->context->has_gen_context); ck_assert_str_eq("system_u", out->context->user); ck_assert_str_eq("object_r", out->context->role); ck_assert_str_eq("bin_t", out->context->type); ck_assert_str_eq("s0", out->context->range); free_fc_entry(out); } END_TEST START_TEST (test_parse_basic_fc_file) { struct policy_node *ast = parse_fc_file(BASIC_FC_FILENAME, NULL); ck_assert_ptr_nonnull(ast); ck_assert_int_eq(ast->flavor, NODE_FC_FILE); ck_assert_ptr_nonnull(ast->next); struct policy_node *cur = ast->next; ck_assert_int_eq(cur->flavor, NODE_FC_ENTRY); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_FC_ENTRY); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_EMPTY); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_ERROR); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_EMPTY); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_FC_ENTRY); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_FC_ENTRY); ck_assert_ptr_null(cur->next); free_policy_node(ast); } END_TEST START_TEST (test_parse_m4) { struct policy_node *ast = parse_fc_file(WITH_M4_FILENAME, NULL); ck_assert_ptr_nonnull(ast); ck_assert_int_eq(ast->flavor, NODE_FC_FILE); ck_assert_ptr_nonnull(ast->next); struct policy_node *cur = ast->next; ck_assert_int_eq(cur->flavor, NODE_FC_ENTRY); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_FC_ENTRY); ck_assert_ptr_nonnull(cur->data.fc_data); struct fc_entry *data = cur->data.fc_data; ck_assert_ptr_nonnull(data->context); ck_assert_str_eq(data->context->type, "hijklmn_t"); ck_assert_ptr_null(cur->next); free_policy_node(ast); } END_TEST START_TEST (test_parse_none_context) { struct policy_node *ast = parse_fc_file(NONE_CONTEXT_FILENAME, NULL); ck_assert_ptr_nonnull(ast); ck_assert_int_eq(ast->flavor, NODE_FC_FILE); ck_assert_ptr_nonnull(ast->next); struct policy_node *cur = ast->next; ck_assert_int_eq(cur->flavor, NODE_FC_ENTRY); ck_assert_ptr_nonnull(cur->next); struct fc_entry *data = cur->data.fc_data; ck_assert_ptr_null(data->context); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_FC_ENTRY); ck_assert_ptr_null(cur->next); data = cur->data.fc_data; ck_assert_ptr_null(data->context); free_policy_node(ast); } END_TEST START_TEST (test_check_for_fc_macro) { ck_assert_int_eq(0, check_for_fc_macro("foo(`bar')", NULL)); struct string_list *cfm = calloc(1, sizeof(struct string_list)); cfm->string = strdup("foo"); ck_assert_int_eq(1, check_for_fc_macro("foo(`bar')", cfm)); ck_assert_int_eq(0, check_for_fc_macro("baz(`bar')", cfm)); ck_assert_int_eq(0, check_for_fc_macro("a", cfm)); free_string_list(cfm); } END_TEST static Suite *parse_fc_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Parse_fc"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_parse_context); tcase_add_test(tc_core, test_parse_context_missing_field); tcase_add_test(tc_core, test_parse_fc_empty); tcase_add_test(tc_core, test_parse_fc_line_with_gen_context); tcase_add_test(tc_core, test_parse_fc_line); tcase_add_test(tc_core, test_parse_fc_line_with_obj); tcase_add_test(tc_core, test_parse_basic_fc_file); tcase_add_test(tc_core, test_parse_m4); tcase_add_test(tc_core, test_parse_none_context); tcase_add_test(tc_core, test_check_for_fc_macro); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = parse_fc_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_parse_functions.c000066400000000000000000000362311475050262500206740ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "../src/parse_functions.h" #include "../src/maps.h" #define EXAMPLE_TYPE_1 "foo_t" #define EXAMPLE_TYPE_2 "bar_t" #define EXAMPLE_TYPE_3 "baz_t" START_TEST (test_insert_header) { struct policy_node *cur = calloc(1, sizeof(struct policy_node)); cur->flavor = NODE_TE_FILE; ck_assert_int_eq(SELINT_SUCCESS, insert_header(&cur, "example", HEADER_BARE, 1)); ck_assert_ptr_nonnull(cur); ck_assert_ptr_null(cur->parent); ck_assert_ptr_null(cur->next); ck_assert_ptr_nonnull(cur->prev); ck_assert_ptr_null(cur->first_child); ck_assert_int_eq(NODE_HEADER, cur->flavor); ck_assert_int_eq(cur->data.h_data->flavor, HEADER_BARE); ck_assert_str_eq(cur->data.h_data->module_name, "example"); ck_assert_int_eq(cur->lineno, 1); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(cur->prev)); cleanup_parsing(); } END_TEST START_TEST (test_insert_comment) { struct policy_node *cur = calloc(1, sizeof(struct policy_node)); cur->flavor = NODE_TE_FILE; struct policy_node *prev = cur; ck_assert_int_eq(SELINT_SUCCESS, insert_comment(&cur, 12345)); ck_assert_ptr_nonnull(cur); ck_assert_ptr_eq(cur->prev, prev); ck_assert_int_eq(cur->flavor, NODE_COMMENT); ck_assert_int_eq(cur->lineno, 12345); ck_assert_ptr_null(cur->data.str); ck_assert_int_eq(SELINT_SUCCESS,free_policy_node(prev)); } END_TEST START_TEST (test_insert_declaration) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); cur->flavor = NODE_TE_FILE; cur->parent = NULL; cur->data.d_data = NULL; cur->first_child = NULL; cur->next = NULL; struct policy_node *prev = cur; set_current_module_name("test"); struct string_list *attrs = calloc(1, sizeof(struct string_list)); ck_assert_int_eq(SELINT_SUCCESS, insert_declaration(&cur, DECL_TYPE, "foo_t", attrs, 1234)); ck_assert_ptr_nonnull(cur); ck_assert_ptr_null(cur->parent); ck_assert_ptr_eq(cur->prev, prev); ck_assert_int_eq(cur->flavor, NODE_DECL); ck_assert_int_eq(cur->lineno, 1234); ck_assert_ptr_null(cur->first_child); ck_assert_ptr_null(prev->first_child); ck_assert_ptr_nonnull(cur->data.d_data); ck_assert_int_eq(cur->data.d_data->flavor, DECL_TYPE); ck_assert_str_eq(cur->data.d_data->name, "foo_t"); ck_assert_ptr_eq(cur->data.d_data->attrs, attrs); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(prev)); // TODO attributes const char *mn = look_up_in_decl_map("foo_t", DECL_TYPE); ck_assert_ptr_nonnull(mn); cleanup_parsing(); } END_TEST START_TEST (test_insert_aliases) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); cur->flavor = NODE_DECL; struct policy_node *orig = cur; struct string_list *aliases = calloc(1, sizeof(struct string_list)); aliases->string = strdup("foo_t"); aliases->next = calloc(1, sizeof(struct string_list)); aliases->next->string = strdup("bar_t"); aliases->next->next = NULL; set_current_module_name("test"); ck_assert_int_eq(SELINT_SUCCESS, insert_aliases(&cur, aliases, DECL_TYPE, 123)); ck_assert_ptr_eq(cur, orig); ck_assert_ptr_nonnull(cur->first_child); cur = cur->first_child; ck_assert_int_eq(cur->flavor, NODE_ALIAS); ck_assert_str_eq(cur->data.str, "foo_t"); ck_assert_ptr_null(cur->prev); ck_assert_ptr_eq(cur->parent, orig); ck_assert_ptr_nonnull(cur->next); ck_assert_int_eq(cur->lineno, 123); cur = cur->next; ck_assert_int_eq(cur->flavor, NODE_ALIAS); ck_assert_str_eq(cur->data.str, "bar_t"); ck_assert_ptr_nonnull(cur->prev); ck_assert_ptr_eq(cur->parent, orig); ck_assert_ptr_null(cur->next); ck_assert_int_eq(cur->lineno, 123); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(orig)); ck_assert_ptr_nonnull(look_up_in_decl_map("foo_t", DECL_TYPE)); ck_assert_ptr_nonnull(look_up_in_decl_map("bar_t", DECL_TYPE)); cleanup_parsing(); } END_TEST START_TEST (test_insert_type_alias) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); struct policy_node *orig = cur; ck_assert_int_eq(SELINT_SUCCESS, insert_type_alias(&cur, "foo_t", 123)); ck_assert_ptr_nonnull(cur); ck_assert_ptr_eq(cur->prev, orig); ck_assert_int_eq(cur->flavor, NODE_TYPE_ALIAS); ck_assert_int_eq(cur->lineno, 123); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(orig)); cleanup_parsing(); } END_TEST START_TEST (test_insert_av_rule) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); struct policy_node *head = cur; ck_assert_int_eq(SELINT_SUCCESS, insert_av_rule(&cur, AV_RULE_AUDITALLOW, NULL, NULL, NULL, NULL, 1234)); ck_assert_ptr_nonnull(cur); ck_assert_int_eq(NODE_AV_RULE, cur->flavor); struct av_rule_data *avd = cur->data.av_data; ck_assert_int_eq(AV_RULE_AUDITALLOW, avd->flavor); ck_assert_int_eq(cur->lineno, 1234); ck_assert_ptr_null(avd->sources); ck_assert_ptr_null(avd->targets); ck_assert_ptr_null(avd->object_classes); ck_assert_ptr_null(avd->perms); free_policy_node(head); cleanup_parsing(); } END_TEST START_TEST (test_insert_role_allow) { struct policy_node *cur = calloc(1, sizeof(struct policy_node)); struct policy_node *head = cur; struct string_list *sl1 = sl_from_str("staff_r"); struct string_list *sl2 = sl_from_str("dbadm_r"); ck_assert_int_eq(SELINT_SUCCESS, insert_role_allow(&cur, sl1, sl2, 20)); ck_assert_ptr_nonnull(cur); ck_assert_int_eq(NODE_ROLE_ALLOW, cur->flavor); struct role_allow_data *ra = cur->data.ra_data; ck_assert_str_eq("staff_r", ra->from->string); ck_assert_ptr_null(ra->from->next); ck_assert_str_eq("dbadm_r", ra->to->string); ck_assert_ptr_null(ra->to->next); free_policy_node(head); cleanup_parsing(); } END_TEST START_TEST (test_insert_type_transition) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); struct policy_node *head = cur; ck_assert_int_eq(SELINT_SUCCESS, insert_type_transition(&cur, TT_TT, NULL, NULL, NULL, "example_tmp_t", NULL, 1234)); ck_assert_ptr_nonnull(cur); ck_assert_int_eq(NODE_TT_RULE, cur->flavor); ck_assert_int_eq(cur->lineno, 1234); struct type_transition_data *ttd = cur->data.tt_data; ck_assert_ptr_null(ttd->sources); ck_assert_ptr_null(ttd->targets); ck_assert_ptr_null(ttd->object_classes); ck_assert_str_eq("example_tmp_t", ttd->default_type); ck_assert_ptr_null(ttd->name); free_policy_node(head); cleanup_parsing(); } END_TEST START_TEST (test_insert_named_type_transition) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); struct policy_node *head = cur; ck_assert_int_eq(SELINT_SUCCESS, insert_type_transition(&cur, TT_TT, NULL, NULL, NULL, "example_tmp_t", "filename.txt", 1234)); ck_assert_ptr_nonnull(cur); ck_assert_int_eq(NODE_TT_RULE, cur->flavor); ck_assert_int_eq(cur->lineno, 1234); struct type_transition_data *ttd = cur->data.tt_data; ck_assert_ptr_null(ttd->sources); ck_assert_ptr_null(ttd->targets); ck_assert_ptr_null(ttd->object_classes); ck_assert_str_eq("example_tmp_t", ttd->default_type); ck_assert_str_eq("filename.txt", ttd->name); free_policy_node(head); cleanup_parsing(); } END_TEST START_TEST (test_insert_interface_call) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); struct policy_node *head = cur; struct string_list *args = calloc(1, sizeof(struct string_list)); args->string = strdup("foo_t"); args->next = calloc(1, sizeof(struct string_list)); args->next->string = strdup("bar_t"); args->next->next = NULL; ck_assert_int_eq(SELINT_SUCCESS, insert_interface_call(&cur, "do_things", args, 1234)); ck_assert_ptr_nonnull(cur); ck_assert_int_eq(NODE_IF_CALL, cur->flavor); ck_assert_int_eq(cur->lineno, 1234); ck_assert_ptr_null(cur->next); ck_assert_ptr_null(cur->parent); ck_assert_ptr_null(cur->first_child); ck_assert_ptr_nonnull(cur->prev); struct if_call_data *if_data = cur->data.ic_data; ck_assert_str_eq("do_things", if_data->name); ck_assert_str_eq("foo_t", if_data->args->string); ck_assert_str_eq("bar_t", if_data->args->next->string); free_policy_node(head); cleanup_parsing(); } END_TEST START_TEST (test_insert_permissive_statement) { struct policy_node *cur = calloc(1, sizeof(struct policy_node)); struct policy_node *head = cur; ck_assert_int_eq(SELINT_SUCCESS, insert_permissive_statement(&cur, "unconfined_t", 5678)); ck_assert_ptr_nonnull(cur); ck_assert_int_eq(NODE_PERMISSIVE, cur->flavor); ck_assert_int_eq(5678, cur->lineno); ck_assert_ptr_null(cur->next); ck_assert_ptr_null(cur->parent); ck_assert_ptr_null(cur->first_child); ck_assert_ptr_eq(cur->prev, head); ck_assert_str_eq("unconfined_t", cur->data.str); free_policy_node(head); cleanup_parsing(); } END_TEST START_TEST (test_optional_policy) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); cur->flavor = NODE_TE_FILE; struct policy_node *head = cur; ck_assert_int_eq(SELINT_SUCCESS, begin_optional_policy(&cur, 1234)); ck_assert_ptr_nonnull(cur); ck_assert_ptr_nonnull(cur->parent); ck_assert_ptr_eq(cur->parent->prev, head); ck_assert_int_eq(cur->flavor, NODE_START_BLOCK); ck_assert_int_eq(cur->parent->flavor, NODE_OPTIONAL_POLICY); ck_assert_ptr_eq(cur->parent->first_child, cur); ck_assert_int_eq(cur->lineno, 1234); ck_assert_int_eq(cur->parent->lineno, 1234); ck_assert_ptr_null(cur->next); ck_assert_ptr_null(cur->prev); ck_assert_ptr_null(cur->first_child); ck_assert_ptr_null(cur->parent->next); ck_assert_int_eq(SELINT_SUCCESS, end_optional_policy(&cur)); ck_assert_ptr_nonnull(cur); ck_assert_ptr_eq(cur->prev, head); ck_assert_int_eq(cur->flavor, NODE_OPTIONAL_POLICY); ck_assert_ptr_nonnull(cur->first_child); ck_assert_ptr_null(cur->first_child->prev); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(head)); cleanup_parsing(); } END_TEST START_TEST (test_interface_def) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); cur->flavor = NODE_TE_FILE; struct policy_node *head = cur; set_current_module_name("test"); ck_assert_int_eq(SELINT_SUCCESS, begin_interface_def(&cur, NODE_INTERFACE_DEF, "foo_read_conf", 1234)); ck_assert_ptr_nonnull(cur); ck_assert_ptr_nonnull(cur->parent); ck_assert_ptr_eq(cur->parent->prev, head); ck_assert_int_eq(cur->flavor, NODE_START_BLOCK); ck_assert_int_eq(cur->parent->flavor, NODE_INTERFACE_DEF); ck_assert_ptr_eq(cur->parent->first_child, cur); ck_assert_str_eq(cur->parent->data.str, "foo_read_conf"); ck_assert_int_eq(cur->lineno, 1234); ck_assert_int_eq(cur->parent->lineno, 1234); ck_assert_ptr_null(cur->next); ck_assert_ptr_null(cur->prev); ck_assert_ptr_null(cur->first_child); ck_assert_ptr_null(cur->parent->next); ck_assert_int_eq(SELINT_SUCCESS, end_interface_def(&cur)); ck_assert_int_eq(SELINT_BAD_ARG, begin_interface_def(&cur, NODE_DECL, "foo_read_conf", 2345)); ck_assert_ptr_nonnull(cur); ck_assert_ptr_eq(cur->prev, head); ck_assert_int_eq(cur->flavor, NODE_INTERFACE_DEF); ck_assert_int_eq(cur->lineno, 1234); ck_assert_ptr_nonnull(cur->first_child); ck_assert_int_eq(cur->first_child->lineno, 1234); ck_assert_ptr_null(cur->first_child->prev); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(head)); cleanup_parsing(); } END_TEST START_TEST (test_wrong_block_end) { struct policy_node *cur = malloc(sizeof(struct policy_node)); memset(cur, 0, sizeof(struct policy_node)); cur->flavor = NODE_TE_FILE; struct policy_node *head = cur; set_current_module_name("test"); ck_assert_int_eq(SELINT_SUCCESS, begin_optional_policy(&cur, 1234)); ck_assert_int_eq(SELINT_NOT_IN_BLOCK, end_interface_def(&cur)); ck_assert_int_eq(SELINT_SUCCESS, end_optional_policy(&cur)); ck_assert_int_eq(SELINT_NOT_IN_BLOCK, end_optional_policy(&cur)); ck_assert_int_eq(SELINT_SUCCESS, begin_interface_def(&cur, NODE_INTERFACE_DEF, "sample_interface", 1235)); ck_assert_int_eq(SELINT_NOT_IN_BLOCK, end_optional_policy(&cur)); ck_assert_int_eq(SELINT_NOT_IN_BLOCK, end_gen_require(&cur, 0)); ck_assert_int_eq(SELINT_SUCCESS, end_interface_def(&cur)); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(head)); cleanup_parsing(); } END_TEST START_TEST (test_save_command) { struct policy_node *cur = calloc(1, sizeof(struct policy_node)); ck_assert_int_eq(SELINT_BAD_ARG, save_command(NULL, "foo")); ck_assert_int_eq(SELINT_SUCCESS, save_command(cur, NULL)); ck_assert_int_eq(SELINT_PARSE_ERROR, save_command(cur, "foo")); ck_assert_int_eq(SELINT_PARSE_ERROR, save_command(cur, "selint-fake:W-001")); ck_assert_ptr_null(cur->exceptions); ck_assert_int_eq(SELINT_SUCCESS, save_command(cur, "selint-disable:W-001")); ck_assert_str_eq("W-001", cur->exceptions); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(cur)); cleanup_parsing(); } END_TEST START_TEST (test_insert_type_attribute) { struct policy_node *head = calloc(1, sizeof(struct policy_node)); struct policy_node *cur = head; struct string_list *attrs = calloc(1, sizeof(struct string_list)); attrs->string = strdup("foo"); ck_assert_int_eq(SELINT_SUCCESS, insert_type_attribute(&cur, "foo_t", attrs, 1234)); ck_assert_ptr_eq(cur->prev, head); ck_assert_str_eq(cur->data.at_data->type, "foo_t"); ck_assert_str_eq(cur->data.at_data->attrs->string, "foo"); free_policy_node(head); cleanup_parsing(); } END_TEST static Suite *parse_functions_suite(void) { Suite *s; TCase *tc_core, *tc_blocks; s = suite_create("Parse_Functions"); tc_core = tcase_create("Core"); tc_blocks = tcase_create("Blocks"); tcase_add_test(tc_core, test_insert_header); tcase_add_test(tc_core, test_insert_comment); tcase_add_test(tc_core, test_insert_declaration); tcase_add_test(tc_core, test_insert_aliases); tcase_add_test(tc_core, test_insert_type_alias); tcase_add_test(tc_core, test_insert_av_rule); tcase_add_test(tc_core, test_insert_role_allow); tcase_add_test(tc_core, test_insert_type_transition); tcase_add_test(tc_core, test_insert_named_type_transition); tcase_add_test(tc_core, test_insert_interface_call); tcase_add_test(tc_core, test_insert_permissive_statement); tcase_add_test(tc_core, test_save_command); tcase_add_test(tc_core, test_insert_type_attribute); suite_add_tcase(s, tc_core); tcase_add_test(tc_blocks, test_optional_policy); tcase_add_test(tc_blocks, test_interface_def); tcase_add_test(tc_blocks, test_wrong_block_end); suite_add_tcase(s, tc_blocks); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = parse_functions_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_parsing.c000066400000000000000000001100021475050262500171220ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../src/tree.h" #include "../src/parse.h" #include "../src/parse_functions.h" #define POLICIES_DIR SAMPLE_POL_DIR #define BASIC_TE_FILENAME POLICIES_DIR "basic.te" #define BASIC_IF_FILENAME POLICIES_DIR "basic.if" #define UNCOMMON_TE_FILENAME POLICIES_DIR "uncommon.te" #define IFDEF_IF_FILENAME POLICIES_DIR "ifdef.if" #define BLOCKS_TE_FILENAME POLICIES_DIR "blocks.te" #define EMPTY_TE_FILENAME POLICIES_DIR "empty.te" #define SYNTAX_ERROR_FILENAME POLICIES_DIR "syntax_error.te" #define BAD_RA_FILENAME POLICIES_DIR "bad_role_allow.te" #define DISABLE_BOOLTUNABLE_TE_FILENAME POLICIES_DIR "disable_booltunable.te" #define DISABLE_COMMENT_TE_FILENAME POLICIES_DIR "disable_comment.te" #define DISABLE_COMMENT_IF_FILENAME POLICIES_DIR "disable_comment.if" #define DISABLE_REQUIRE_IF_FILENAME POLICIES_DIR "disable_require.if" #define BOOL_DECLARATION_FILENAME POLICIES_DIR "bool_declarations.te" #define EXTENDED_TE_FILENAME POLICIES_DIR "extended_perms.te" #define IFDEF_BLOCK_FILENAME POLICIES_DIR "ifdef_block.te" START_TEST (test_parse_basic_te) { set_current_module_name("basic"); FILE *f = fopen(BASIC_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, BASIC_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast); struct policy_node *current = ast; ck_assert_ptr_nonnull(current); ck_assert_int_eq(NODE_TE_FILE, current->flavor); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = ast->next; ck_assert_int_eq(NODE_HEADER, current->flavor); struct header_data *hd = current->data.h_data; ck_assert_int_eq(HEADER_MACRO, hd->flavor); ck_assert_str_eq("basic", hd->module_name); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); struct declaration_data *dd = current->data.d_data; ck_assert_int_eq(DECL_TYPE, dd->flavor); ck_assert_str_eq("basic_t", dd->name); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); dd = current->data.d_data; ck_assert_int_eq(DECL_TYPE, dd->flavor); ck_assert_str_eq("basic_exec_t", dd->name); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_AV_RULE, current->flavor); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_IF_CALL, current->flavor); struct if_call_data *icd = current->data.ic_data; ck_assert_str_eq("macro1", icd->name); struct string_list *args = icd->args; ck_assert_str_eq("basic_t", args->string); ck_assert_int_eq(0, args->has_incorrect_space); ck_assert_int_eq(1, args->arg_start); ck_assert_ptr_null(args->next); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_IF_CALL, current->flavor); icd = current->data.ic_data; ck_assert_str_eq("macro2", icd->name); args = icd->args; ck_assert_str_eq("basic_t", args->string); ck_assert_int_eq(0, args->has_incorrect_space); ck_assert_int_eq(1, args->arg_start); ck_assert_ptr_nonnull(args->next); args = args->next; ck_assert_str_eq("basic_exec_t", args->string); ck_assert_int_eq(0, args->has_incorrect_space); ck_assert_int_eq(1, args->arg_start); ck_assert_ptr_null(args->next); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_IF_CALL, current->flavor); icd = current->data.ic_data; ck_assert_str_eq("macro3", icd->name); args = icd->args; ck_assert_str_eq("basic_t", args->string); ck_assert_int_eq(0, args->has_incorrect_space); ck_assert_int_eq(1, args->arg_start); ck_assert_ptr_nonnull(args->next); args = args->next; ck_assert_str_eq("basic_exec_t", args->string); ck_assert_int_eq(1, args->has_incorrect_space); ck_assert_int_eq(0, args->arg_start); ck_assert_ptr_null(args->next); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_IF_CALL, current->flavor); icd = current->data.ic_data; ck_assert_str_eq("macro4", icd->name); args = icd->args; ck_assert_str_eq("basic_t", args->string); ck_assert_int_eq(0, args->has_incorrect_space); ck_assert_int_eq(1, args->arg_start); ck_assert_ptr_nonnull(args->next); args = args->next; ck_assert_str_eq("basic_t", args->string); ck_assert_int_eq(0, args->has_incorrect_space); ck_assert_int_eq(1, args->arg_start); ck_assert_ptr_nonnull(args->next); args = args->next; ck_assert_str_eq("basic_exec_t", args->string); ck_assert_int_eq(1, args->has_incorrect_space); ck_assert_int_eq(0, args->arg_start); ck_assert_ptr_nonnull(args->next); args = args->next; ck_assert_str_eq("basic_t", args->string); ck_assert_int_eq(0, args->has_incorrect_space); ck_assert_int_eq(1, args->arg_start); ck_assert_ptr_nonnull(args->next); args = args->next; ck_assert_str_eq("-basic_exec_t", args->string); ck_assert_int_eq(0, args->has_incorrect_space); ck_assert_int_eq(0, args->arg_start); ck_assert_ptr_null(args->next); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_OPTIONAL_POLICY, current->flavor); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_nonnull(current->first_child); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_str_eq("basic", look_up_in_decl_map("basic_t", DECL_TYPE)); ck_assert_int_eq(2, decl_map_count(DECL_TYPE)); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_parse_basic_if) { set_current_module_name("basic"); FILE *f = fopen(BASIC_IF_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, BASIC_IF_FILENAME, NODE_IF_FILE); ck_assert_ptr_nonnull(ast); struct policy_node *current = ast; ck_assert_ptr_nonnull(current); ck_assert_int_eq(NODE_IF_FILE, current->flavor); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_COMMENT, current->flavor); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_COMMENT, current->flavor); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_INTERFACE_DEF, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_str_eq("basic_domtrans", current->data.str); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(current->flavor, NODE_GEN_REQ); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_nonnull(current->prev); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_str_eq("basic_t", current->data.d_data->name); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->prev); ck_assert_ptr_nonnull(current->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_str_eq("basic_exec_t", current->data.d_data->name); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->prev); ck_assert_ptr_nonnull(current->parent); ck_assert_ptr_null(current->next); current = current->parent->next; ck_assert_int_eq(NODE_IF_CALL, current->flavor); ck_assert_ptr_nonnull(current->parent); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_parse_uncommon_constructs) { set_current_module_name("uncommon"); FILE *f = fopen(UNCOMMON_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, UNCOMMON_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast); ck_assert_ptr_nonnull(ast); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_parse_interface_ifdef) { set_current_module_name("ifdef"); FILE *f = fopen(IFDEF_IF_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, IFDEF_IF_FILENAME, NODE_IF_FILE); ck_assert_ptr_nonnull(ast); ck_assert_ptr_nonnull(ast); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_parse_blocks) { set_current_module_name("blocks"); FILE *f = fopen(BLOCKS_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, BLOCKS_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast); ck_assert_ptr_nonnull(ast); struct policy_node *current = ast; ck_assert_int_eq(NODE_TE_FILE, current->flavor); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_HEADER, current->flavor); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_OPTIONAL_POLICY, current->flavor); ck_assert_ptr_null(current->next); ck_assert_ptr_nonnull(current->first_child); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->next); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_parse_empty_file) { set_current_module_name("empty"); FILE *f = fopen(EMPTY_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, EMPTY_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast); ck_assert_int_eq(NODE_EMPTY, ast->flavor); ck_assert_ptr_null(ast->next); ck_assert_ptr_null(ast->first_child); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_syntax_error) { set_current_module_name("syntax_error"); FILE *f = fopen(SYNTAX_ERROR_FILENAME, "r"); ck_assert_ptr_nonnull(f); ck_assert_ptr_null(yyparse_wrapper(f, SYNTAX_ERROR_FILENAME, NODE_TE_FILE)); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_parse_bad_role_allow) { set_current_module_name("bad_ra"); FILE *f = fopen(BAD_RA_FILENAME, "r"); ck_assert_ptr_nonnull(f); ck_assert_ptr_null(yyparse_wrapper(f, BAD_RA_FILENAME, NODE_TE_FILE)); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_disable_booltunable_te) { set_current_module_name("disable_booltunable"); FILE *f = fopen(DISABLE_BOOLTUNABLE_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, DISABLE_BOOLTUNABLE_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast); const struct policy_node *cur = ast; ck_assert_ptr_nonnull(cur); ck_assert_int_eq(NODE_TE_FILE, cur->flavor); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(NODE_HEADER, cur->flavor); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(NODE_DECL, cur->flavor); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(NODE_TUNABLE_POLICY, cur->flavor); ck_assert_str_eq("C-008", cur->exceptions); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(NODE_TUNABLE_POLICY, cur->flavor); ck_assert_str_eq("C-008", cur->exceptions); ck_assert_ptr_nonnull(cur->next); cur = cur->next; ck_assert_int_eq(NODE_BOOLEAN_POLICY, cur->flavor); ck_assert_str_eq("C-008", cur->exceptions); ck_assert_ptr_null(cur->next); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_disable_comment_te) { set_current_module_name("disable_comment"); FILE *f = fopen(DISABLE_COMMENT_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, DISABLE_COMMENT_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast); ck_assert_ptr_nonnull(ast); ck_assert_int_eq(NODE_TE_FILE, ast->flavor); ck_assert_ptr_nonnull(ast->next); ck_assert_int_eq(NODE_HEADER, ast->next->flavor); ck_assert_ptr_nonnull(ast->next->next); ck_assert_int_eq(NODE_DECL, ast->next->next->flavor); ck_assert_ptr_nonnull(ast->next->next->next); ck_assert_int_eq(NODE_AV_RULE, ast->next->next->next->flavor); ck_assert_str_eq("W-001", ast->next->next->next->exceptions); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_disable_comment_if) { set_current_module_name("disable_comment"); FILE *f = fopen(DISABLE_COMMENT_IF_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, DISABLE_COMMENT_IF_FILENAME, NODE_IF_FILE); ck_assert_ptr_nonnull(ast); ck_assert_ptr_nonnull(ast); ck_assert_int_eq(NODE_IF_FILE, ast->flavor); ck_assert_ptr_nonnull(ast->next); ck_assert_int_eq(NODE_INTERFACE_DEF, ast->next->flavor); ck_assert_str_eq("S-012", ast->next->exceptions); ck_assert_ptr_null(ast->next->next); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_disable_require_if) { set_current_module_name("disable_require"); FILE *f = fopen(DISABLE_REQUIRE_IF_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, DISABLE_REQUIRE_IF_FILENAME, NODE_IF_FILE); ck_assert_ptr_nonnull(ast); const struct policy_node *current = ast; // top file node ck_assert_ptr_nonnull(current); ck_assert_int_eq(NODE_IF_FILE, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_nonnull(current->next); // first interface current = current->next; ck_assert_int_eq(NODE_INTERFACE_DEF, current->flavor); ck_assert_str_eq("foo1", current->data.str); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_nonnull(current->first_child); // start block current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); // require block current = current->next; ck_assert_int_eq(NODE_GEN_REQ, current->flavor); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_null(current->next); ck_assert_ptr_nonnull(current->first_child); // start block current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); // first declaration current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_int_eq(DECL_CLASS, current->data.d_data->flavor); ck_assert_str_eq("bar1_c", current->data.d_data->name); ck_assert_ptr_nonnull(current->data.d_data->attrs); ck_assert_str_eq(" W-010", current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); // second declaration current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_int_eq(DECL_ROLE, current->data.d_data->flavor); ck_assert_str_eq("bar1_r", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); ck_assert_str_eq(" W-011", current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); // third declaration current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_int_eq(DECL_BOOL, current->data.d_data->flavor); ck_assert_str_eq("bar1_b", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); ck_assert_str_eq(" W-012", current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_null(current->next); // second interface current = current->parent->parent->next; ck_assert_int_eq(NODE_INTERFACE_DEF, current->flavor); ck_assert_str_eq("foo2", current->data.str); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_nonnull(current->first_child); // start block current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); // require block current = current->next; ck_assert_int_eq(NODE_GEN_REQ, current->flavor); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_null(current->next); ck_assert_ptr_nonnull(current->first_child); // start block current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); // first declaration current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_int_eq(DECL_TYPE, current->data.d_data->flavor); ck_assert_str_eq("bar3_t", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); ck_assert_str_eq(" W-011", current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); // second declaration current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_int_eq(DECL_TYPE, current->data.d_data->flavor); ck_assert_str_eq("bar4_t", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); ck_assert_str_eq(" W-011", current->exceptions); ck_assert_ptr_null(current->first_child); ck_assert_ptr_null(current->next); // cleanup free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_bool_declarations) { set_current_module_name("bool_declarations"); FILE *f = fopen(BOOL_DECLARATION_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, BOOL_DECLARATION_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast); struct policy_node *current = ast; // top file node ck_assert_ptr_nonnull(current); ck_assert_int_eq(NODE_TE_FILE, current->flavor); ck_assert_ptr_nonnull(current->next); current = current->next; // header node ck_assert_ptr_null(current->parent); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_nonnull(current->prev); ck_assert_ptr_null(current->first_child); ck_assert_int_eq(NODE_HEADER, current->flavor); current = current->next; // first bool ck_assert_ptr_null(current->parent); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_nonnull(current->prev); ck_assert_ptr_null(current->first_child); ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_ptr_nonnull(current->data.d_data); ck_assert_int_eq(DECL_BOOL, current->data.d_data->flavor); ck_assert_str_eq("bool_one", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); current = current->next; // second bool ck_assert_ptr_null(current->parent); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_nonnull(current->prev); ck_assert_ptr_null(current->first_child); ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_ptr_nonnull(current->data.d_data); ck_assert_int_eq(DECL_BOOL, current->data.d_data->flavor); ck_assert_str_eq("bool_two", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); current = current->next; // third bool ck_assert_ptr_null(current->parent); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_nonnull(current->prev); ck_assert_ptr_null(current->first_child); ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_ptr_nonnull(current->data.d_data); ck_assert_int_eq(DECL_BOOL, current->data.d_data->flavor); ck_assert_str_eq("bool_three", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); current = current->next; // first tunable ck_assert_ptr_null(current->parent); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_nonnull(current->prev); ck_assert_ptr_null(current->first_child); ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_ptr_nonnull(current->data.d_data); ck_assert_int_eq(DECL_BOOL, current->data.d_data->flavor); ck_assert_str_eq("tunable_one", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); current = current->next; // second tunable ck_assert_ptr_null(current->parent); ck_assert_ptr_null(current->next); // last node ck_assert_ptr_nonnull(current->prev); ck_assert_ptr_null(current->first_child); ck_assert_int_eq(NODE_DECL, current->flavor); ck_assert_ptr_nonnull(current->data.d_data); ck_assert_int_eq(DECL_BOOL, current->data.d_data->flavor); ck_assert_str_eq("tunable_two", current->data.d_data->name); ck_assert_ptr_null(current->data.d_data->attrs); // check storage const char *mod_name; mod_name = look_up_in_decl_map("bool_one", DECL_BOOL); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq("bool_declarations", mod_name); mod_name = look_up_in_decl_map("bool_two", DECL_BOOL); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq("bool_declarations", mod_name); mod_name = look_up_in_decl_map("bool_three", DECL_BOOL); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq("bool_declarations", mod_name); mod_name = look_up_in_decl_map("tunable_one", DECL_BOOL); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq("bool_declarations", mod_name); mod_name = look_up_in_decl_map("tunable_two", DECL_BOOL); ck_assert_ptr_nonnull(mod_name); ck_assert_str_eq("bool_declarations", mod_name); // some cross checks ck_assert_ptr_null(look_up_in_decl_map("bool_four", DECL_BOOL)); ck_assert_ptr_null(look_up_in_decl_map("bool_one", DECL_TYPE)); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_file_flavor_mismatch) { set_current_module_name("basic"); FILE *f = fopen(BASIC_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f); ck_assert_ptr_null(yyparse_wrapper(f, BASIC_TE_FILENAME, NODE_IF_FILE)); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_extended_perms) { set_current_module_name("extended_perms"); FILE *f = fopen(EXTENDED_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, EXTENDED_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast); struct policy_node *current = ast; ck_assert_ptr_nonnull(current); ck_assert_int_eq(NODE_TE_FILE, current->flavor); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = ast->next; ck_assert_int_eq(NODE_HEADER, current->flavor); struct header_data *hd = current->data.h_data; ck_assert_int_eq(HEADER_MACRO, hd->flavor); ck_assert_str_eq("extended_perms", hd->module_name); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); struct declaration_data *dd = current->data.d_data; ck_assert_int_eq(DECL_TYPE, dd->flavor); ck_assert_str_eq("basic_t", dd->name); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_DECL, current->flavor); dd = current->data.d_data; ck_assert_int_eq(DECL_TYPE, dd->flavor); ck_assert_str_eq("basic_dev_t", dd->name); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_AV_RULE, current->flavor); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_ALLOW, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("~", current->data.xav_data->perms->string); ck_assert_str_eq("0x8927", current->data.xav_data->perms->next->string); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_ALLOW, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("35072", current->data.xav_data->perms->string); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_ALLOW, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("0027", current->data.xav_data->perms->string); ck_assert_str_eq("0028", current->data.xav_data->perms->next->string); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_ALLOW, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("0", current->data.xav_data->perms->string); ck_assert_str_eq("0x00", current->data.xav_data->perms->next->string); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_ALLOW, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("0x0000", current->data.xav_data->perms->string); ck_assert_str_eq("-", current->data.xav_data->perms->next->string); ck_assert_str_eq("0x00ff", current->data.xav_data->perms->next->next->string); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_ALLOW, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("1024", current->data.xav_data->perms->string); ck_assert_str_eq("-", current->data.xav_data->perms->next->string); ck_assert_str_eq("2048", current->data.xav_data->perms->next->next->string); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_DONTAUDIT, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("1024-2048", current->data.xav_data->perms->string); ck_assert_str_eq("35072", current->data.xav_data->perms->next->string); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_AUDITALLOW, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("ioctl_macro", current->data.xav_data->perms->string); ck_assert_ptr_nonnull(current->next); ck_assert_ptr_null(current->first_child); current = current->next; ck_assert_int_eq(NODE_XAV_RULE, current->flavor); ck_assert_int_eq(AV_RULE_NEVERALLOW, current->data.xav_data->flavor); ck_assert_str_eq("ioctl", current->data.xav_data->operation); ck_assert_str_eq("ioctl_macro", current->data.xav_data->perms->string); ck_assert_str_eq("0x40ff-0x41ff", current->data.xav_data->perms->next->string); ck_assert_ptr_null(current->next); ck_assert_ptr_null(current->first_child); ck_assert_str_eq("extended_perms", look_up_in_decl_map("basic_t", DECL_TYPE)); ck_assert_int_eq(2, decl_map_count(DECL_TYPE)); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST START_TEST (test_parse_ifdef) { set_current_module_name("ifdef_block"); FILE *f = fopen(IFDEF_BLOCK_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, IFDEF_BLOCK_FILENAME, NODE_TE_FILE); const struct policy_node *current = ast; const struct policy_node *ifelse_block; ck_assert_ptr_nonnull(current); ck_assert_int_eq(NODE_TE_FILE, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_HEADER, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_IFELSE, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_null(current->next); ifelse_block = current; current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_M4_ARG, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent); ck_assert_ptr_nonnull(current->next); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_M4_SIMPLE_MACRO, current->flavor); ck_assert_str_eq("bool1", current->data.str); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_null(current->next); current = current->parent->next; ck_assert_int_eq(NODE_M4_ARG, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent); ck_assert_ptr_nonnull(current->next); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_M4_SIMPLE_MACRO, current->flavor); ck_assert_str_eq("true", current->data.str); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_null(current->next); current = current->parent->next; ck_assert_int_eq(NODE_M4_ARG, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent); ck_assert_ptr_nonnull(current->next); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_AV_RULE, current->flavor); ck_assert_str_eq("source1", current->data.av_data->sources->string); ck_assert_str_eq("perm1", current->data.av_data->perms->string); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_AV_RULE, current->flavor); ck_assert_str_eq("source1", current->data.av_data->sources->string); ck_assert_str_eq("perm2", current->data.av_data->perms->string); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_null(current->next); current = current->parent->next; ck_assert_int_eq(NODE_M4_ARG, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent); ck_assert_ptr_nonnull(current->next); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_M4_SIMPLE_MACRO, current->flavor); ck_assert_str_eq("bool2", current->data.str); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_null(current->next); current = current->parent->next; ck_assert_int_eq(NODE_M4_ARG, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent); ck_assert_ptr_nonnull(current->next); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_M4_SIMPLE_MACRO, current->flavor); ck_assert_str_eq("true", current->data.str); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_null(current->next); current = current->parent->next; ck_assert_int_eq(NODE_M4_ARG, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent); ck_assert_ptr_nonnull(current->next); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_AV_RULE, current->flavor); ck_assert_str_eq("source2", current->data.av_data->sources->string); ck_assert_str_eq("perm1", current->data.av_data->perms->string); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_AV_RULE, current->flavor); ck_assert_str_eq("source2", current->data.av_data->sources->string); ck_assert_str_eq("perm2", current->data.av_data->perms->string); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_null(current->next); current = current->parent->next; ck_assert_int_eq(NODE_M4_ARG, current->flavor); ck_assert_ptr_nonnull(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent); ck_assert_ptr_null(current->next); current = current->first_child; ck_assert_int_eq(NODE_START_BLOCK, current->flavor); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_AV_RULE, current->flavor); ck_assert_str_eq("source3", current->data.av_data->sources->string); ck_assert_str_eq("perm1", current->data.av_data->perms->string); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_nonnull(current->next); current = current->next; ck_assert_int_eq(NODE_AV_RULE, current->flavor); ck_assert_str_eq("source3", current->data.av_data->sources->string); ck_assert_str_eq("perm2", current->data.av_data->perms->string); ck_assert_ptr_null(current->first_child); ck_assert_ptr_eq(ifelse_block, current->parent->parent); ck_assert_ptr_null(current->next); current = current->parent; ck_assert_ptr_null(current->next); current = current->parent; ck_assert_ptr_eq(ifelse_block, current); ck_assert_ptr_null(current->next); free_policy_node(ast); cleanup_parsing(); fclose(f); } END_TEST static Suite *parsing_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Parsing"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_parse_basic_te); tcase_add_test(tc_core, test_parse_basic_if); tcase_add_test(tc_core, test_parse_uncommon_constructs); tcase_add_test(tc_core, test_parse_interface_ifdef); tcase_add_test(tc_core, test_parse_blocks); tcase_add_test(tc_core, test_parse_empty_file); tcase_add_test(tc_core, test_syntax_error); tcase_add_test(tc_core, test_parse_bad_role_allow); tcase_add_test(tc_core, test_disable_booltunable_te); tcase_add_test(tc_core, test_disable_comment_te); tcase_add_test(tc_core, test_disable_comment_if); tcase_add_test(tc_core, test_disable_require_if); tcase_add_test(tc_core, test_bool_declarations); tcase_add_test(tc_core, test_file_flavor_mismatch); tcase_add_test(tc_core, test_extended_perms); tcase_add_test(tc_core, test_parse_ifdef); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = parsing_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_perm_macro.c000066400000000000000000000337751475050262500176300ustar00rootroot00000000000000/* * Copyright 2020 The SELint Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "../src/startup.h" #include "../src/maps.h" #include "../src/selint_error.h" #include "../src/perm_macro.h" #include "../src/parse_functions.h" #define PERMS_PATH SAMPLE_POL_DIR "perms.spt" typedef uint32_t mask_t; extern void compute_perm_mask(const struct string_list *permissions, mask_t *mask_raw, mask_t *mask_extended); extern unsigned short popcount(mask_t x); START_TEST (test_permmacro_dirs) { enum selint_error res; struct string_list *permissions; char *check_str; mask_t mask_raw, mask_extended; // parse permission macros res = load_obj_perm_sets_source(PERMS_PATH); ck_assert_int_eq(SELINT_SUCCESS, res); ck_assert_ptr_null(permmacro_check("dir", NULL)); // check 0 permissions = sl_from_str("getattr"); ck_assert_ptr_nonnull(permissions); ck_assert_ptr_null(permmacro_check("dir", permissions)); free_string_list(permissions); // check 1 permissions = sl_from_str("search_dir_perms"); ck_assert_ptr_nonnull(permissions); ck_assert_ptr_null(permmacro_check("dir", permissions)); free_string_list(permissions); // check 2 permissions = sl_from_strs(2, "getattr", "search"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(2, popcount(mask_raw)); ck_assert_int_eq(2, popcount(mask_extended)); ck_assert_ptr_null(permmacro_check("dir", permissions)); free_string_list(permissions); //check 3 permissions = sl_from_strs(3, "getattr", "search", "open"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(3, popcount(mask_raw)); ck_assert_int_eq(3, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: search_dir_perms (replacing { getattr search open }, would add (none))", check_str); free(check_str); free_string_list(permissions); // check 4 permissions = sl_from_strs(2, "search", "open"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(2, popcount(mask_raw)); ck_assert_int_eq(3, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: search_dir_perms (replacing { search open }, would add { getattr })", check_str); free(check_str); free_string_list(permissions); // check 5 permissions = sl_from_strs(2, "create", "mounton"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(2, popcount(mask_raw)); ck_assert_int_eq(4, popcount(mask_extended)); ck_assert_ptr_null(permmacro_check("dir", permissions)); free_string_list(permissions); // check 6 permissions = sl_from_strs(5, "open", "read", "write", "remove_name", "add_name"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(5, popcount(mask_raw)); ck_assert_int_eq(10, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: rw_dir_perms (replacing { open read write remove_name add_name }, would add { ioctl getattr lock search })", check_str); free(check_str); free_string_list(permissions); // check 6 permissions = sl_from_strs(2, "search_dir_perms", "read"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(4, popcount(mask_raw)); ck_assert_int_eq(6, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: read_dir_perms (replacing { search_dir_perms read }, would add { ioctl lock })", check_str); free(check_str); free_string_list(permissions); // check 7 permissions = sl_from_strs(3, "search_dir_perms", "read", "quotaon"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(5, popcount(mask_raw)); ck_assert_int_eq(7, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: read_dir_perms (replacing { search_dir_perms read }, would add { ioctl lock })", check_str); free(check_str); free_string_list(permissions); // check 7 permissions = sl_from_strs(3, "search_dir_perms", "read", "some_new_perm"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(5, popcount(mask_raw)); ck_assert_int_eq(7, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: read_dir_perms (replacing { search_dir_perms read }, would add { ioctl lock })", check_str); free(check_str); free_string_list(permissions); // check 8 permissions = sl_from_strs(4, "relabel_dir_perms", "open", "read", "search"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(6, popcount(mask_raw)); ck_assert_int_eq(8, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: read_dir_perms (replacing { open read search }, would add { ioctl lock })", check_str); free(check_str); free_string_list(permissions); // check 9 permissions = sl_from_strs(6, "create", "open", "read", "add_name", "remove_name", "rmdir"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(6, popcount(mask_raw)); ck_assert_int_eq(14, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: control_dir_perms (replacing { create open read add_name remove_name rmdir }, would add { ioctl write getattr setattr lock unlink link rename reparent search })", check_str); free(check_str); free_string_list(permissions); // check 10 permissions = sl_from_strs(3, "search", "open", "some_new_perm"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(3, popcount(mask_raw)); ck_assert_int_eq(4, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: search_dir_perms (replacing { search open }, would add { getattr })", check_str); free(check_str); free_string_list(permissions); // check 11 permissions = sl_from_strs(3, "search", "open", "audit_access"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(3, popcount(mask_raw)); ck_assert_int_eq(4, popcount(mask_extended)); check_str = permmacro_check("dir", permissions); ck_assert_str_eq("Suggesting permission macro: search_dir_perms (replacing { search open }, would add { getattr })", check_str); free(check_str); free_string_list(permissions); // cleanup cleanup_parsing(); } END_TEST START_TEST (test_permmacro_files) { enum selint_error res; struct string_list *permissions; char *check_str; mask_t mask_raw, mask_extended; // parse permission macros res = load_obj_perm_sets_source(PERMS_PATH); ck_assert_int_eq(SELINT_SUCCESS, res); // check 1 ck_assert_ptr_null(permmacro_check("file", NULL)); // check 2 permissions = sl_from_str("getattr"); ck_assert_ptr_nonnull(permissions); ck_assert_ptr_null(permmacro_check("file", permissions)); free_string_list(permissions); // check 3 permissions = sl_from_strs(3, "open", "read", "lock"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(3, popcount(mask_raw)); ck_assert_int_eq(6, popcount(mask_extended)); check_str = permmacro_check("file", permissions); ck_assert_str_eq("Suggesting permission macro: read_file_perms (replacing { open read lock }, would add { ioctl getattr })", check_str); free(check_str); free_string_list(permissions); // check 4 permissions = sl_from_str("read_no_lock_file_perms"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(4, popcount(mask_raw)); ck_assert_int_eq(6, popcount(mask_extended)); check_str = permmacro_check("file", permissions); // do not suggest read_file_perms ck_assert_ptr_null(check_str); free(check_str); free_string_list(permissions); // check 5 permissions = sl_from_strs(2, "map", "read_file_perms"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(6, popcount(mask_raw)); ck_assert_int_eq(7, popcount(mask_extended)); check_str = permmacro_check("file", permissions); // do not 'suggesting mmap_read_file_perms replacing { map } (would add (none))' ck_assert_ptr_null(check_str); free_string_list(permissions); // check 6 permissions = sl_from_strs(2, "relabelfrom", "relabelto"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(2, popcount(mask_raw)); ck_assert_int_eq(3, popcount(mask_extended)); check_str = permmacro_check("file", permissions); ck_assert_str_eq("Suggesting permission macro: relabel_file_perms (replacing { relabelfrom relabelto }, would add { getattr })", check_str); free(check_str); free_string_list(permissions); // check 7 permissions = sl_from_strs(5, "open", "read", "write", "create", "unlink"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(5, popcount(mask_raw)); ck_assert_int_eq(12, popcount(mask_extended)); check_str = permmacro_check("file", permissions); ck_assert_str_eq("Suggesting permission macro: control_file_perms (replacing { open read write create unlink }, would add { ioctl getattr setattr lock append link rename })", check_str); free(check_str); free_string_list(permissions); // check 8 permissions = sl_from_strs(2, "getattr", "rename"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(2, popcount(mask_raw)); ck_assert_int_eq(2, popcount(mask_extended)); check_str = permmacro_check("file", permissions); ck_assert_str_eq("Suggesting permission macro: rename_file_perms (replacing { getattr rename }, would add (none))", check_str); free(check_str); free_string_list(permissions); // check 9 permissions = sl_from_strs(2, "read_file_perms", "write_file_perms"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(6, popcount(mask_raw)); ck_assert_int_eq(8, popcount(mask_extended)); check_str = permmacro_check("file", permissions); ck_assert_str_eq("Suggesting permission macro: rw_file_perms (replacing { read_file_perms write_file_perms }, would add (none))", check_str); free(check_str); free_string_list(permissions); // check 10 permissions = sl_from_strs(3, "read", "write", "open"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(3, popcount(mask_raw)); ck_assert_int_eq(8, popcount(mask_extended)); check_str = permmacro_check("file", permissions); ck_assert_str_eq("Suggesting permission macro: rw_file_perms (replacing { read write open }, would add { ioctl getattr lock })", check_str); free(check_str); free_string_list(permissions); // check 11 permissions = sl_from_strs(2, "read", "write"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(2, popcount(mask_raw)); ck_assert_int_eq(7, popcount(mask_extended)); check_str = permmacro_check("file", permissions); ck_assert_str_eq("Suggesting permission macro: rw_no_open_file_perms (replacing { read write }, would add { ioctl getattr lock })", check_str); free(check_str); free_string_list(permissions); // check 12 permissions = sl_from_strs(3, "read_file_perms", "relabelfrom", "relabelto"); ck_assert_ptr_nonnull(permissions); mask_raw = mask_extended = 0; compute_perm_mask(permissions, &mask_raw, &mask_extended); ck_assert_int_eq(7, popcount(mask_raw)); ck_assert_int_eq(8, popcount(mask_extended)); check_str = permmacro_check("file", permissions); ck_assert_str_eq("Suggesting permission macro: relabel_file_perms (replacing { relabelfrom relabelto }, would add (none))", check_str); free(check_str); free_string_list(permissions); // cleanup cleanup_parsing(); } END_TEST static Suite *startup_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Permmacro"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_permmacro_dirs); tcase_add_test(tc_core, test_permmacro_files); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = startup_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_runner.c000066400000000000000000000047671475050262500170140ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../src/string_list.h" #include "../src/runner.h" START_TEST (test_is_check_enabled) { struct string_list *con_e = calloc(1, sizeof(struct string_list)); con_e->string = strdup("S-001"); struct string_list *con_d = calloc(1, sizeof(struct string_list)); con_d->string = strdup("S-001"); con_d->next = calloc(1, sizeof(struct string_list)); con_d->next->string = strdup("S-002"); con_d->next->next = calloc(1, sizeof(struct string_list)); con_d->next->next->string = strdup("S-003"); struct string_list *cl_e = calloc(1, sizeof(struct string_list)); cl_e->string = strdup("S-002"); struct string_list *cl_d = calloc(1, sizeof(struct string_list)); cl_d->string = strdup("S-004"); ck_assert_int_eq(is_check_enabled("S-001", con_e, con_d, cl_e, cl_d, 0), 1); ck_assert_int_eq(is_check_enabled("S-002", con_e, con_d, cl_e, cl_d, 0), 1); ck_assert_int_eq(is_check_enabled("S-003", con_e, con_d, cl_e, cl_d, 0), 0); ck_assert_int_eq(is_check_enabled("S-004", con_e, con_d, cl_e, cl_d, 0), 0); ck_assert_int_eq(is_check_enabled("S-001", con_e, con_d, cl_e, cl_d, 1), 0); ck_assert_int_eq(is_check_enabled("S-002", con_e, con_d, cl_e, cl_d, 1), 1); ck_assert_int_eq(is_check_enabled("S-003", con_e, con_d, cl_e, cl_d, 1), 0); ck_assert_int_eq(is_check_enabled("S-004", con_e, con_d, cl_e, cl_d, 1), 0); free_string_list(con_e); free_string_list(con_d); free_string_list(cl_e); free_string_list(cl_d); } END_TEST static Suite *runner_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Runner"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_is_check_enabled); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = runner_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_selint_config.c000066400000000000000000000147651475050262500203250ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "../src/string_list.h" #include "../src/selint_config.h" #include "../src/ordering.h" #define CONFIGS_DIR SAMPLE_CONF_DIR #define SEVERITY_CONVENTION_FILENAME CONFIGS_DIR "severity_convention.conf" #define SEVERITY_STYLE_FILENAME CONFIGS_DIR "severity_style.conf" #define SEVERITY_WARNING_FILENAME CONFIGS_DIR "severity_warning.conf" #define SEVERITY_ERROR_FILENAME CONFIGS_DIR "severity_error.conf" #define SEVERITY_FATAL_FILENAME CONFIGS_DIR "severity_fatal.conf" #define CHECKS_FILENAME CONFIGS_DIR "check_config.conf" #define REFPOL_ORDERING_FILENAME CONFIGS_DIR "refpolicy_ordering.conf" #define ORDER_REQUIRES_FILENAME CONFIGS_DIR "order_requires.conf" #define BAD_FORMAT_1 CONFIGS_DIR "bad_format.conf" #define BAD_FORMAT_2 CONFIGS_DIR "bad_format_2.conf" #define BAD_OPTION CONFIGS_DIR "invalid_option.conf" #define BAD_SEVERITY CONFIGS_DIR "severity_invalid.conf" #define BAD_ORDER CONFIGS_DIR "bad_order.conf" START_TEST (test_parse_config_severity) { char severity = '\0'; struct config_check_data ccd; ck_assert_int_eq(SELINT_SUCCESS, parse_config(SEVERITY_CONVENTION_FILENAME, 0, &severity, NULL, NULL, NULL, &ccd)); ck_assert_int_eq('C', severity); ck_assert_int_eq(SELINT_SUCCESS, parse_config(SEVERITY_STYLE_FILENAME, 0, &severity, NULL, NULL, NULL, &ccd)); ck_assert_int_eq('S', severity); ck_assert_int_eq(SELINT_SUCCESS, parse_config(SEVERITY_WARNING_FILENAME, 0, &severity, NULL, NULL, NULL, &ccd)); ck_assert_int_eq('W', severity); ck_assert_int_eq(SELINT_SUCCESS, parse_config(SEVERITY_ERROR_FILENAME, 0, &severity, NULL, NULL, NULL, &ccd)); ck_assert_int_eq('E', severity); ck_assert_int_eq(SELINT_SUCCESS, parse_config(SEVERITY_FATAL_FILENAME, 0, &severity, NULL, NULL, NULL, &ccd)); ck_assert_int_eq('F', severity); } END_TEST START_TEST (test_parse_config_checks) { char severity = '\0'; struct string_list *dis = NULL; struct string_list *en = NULL; struct string_list *cfm = NULL; struct config_check_data ccd; ck_assert_int_eq(SELINT_SUCCESS, parse_config(CHECKS_FILENAME, 0, &severity, &dis, &en, &cfm, &ccd)); ck_assert_ptr_nonnull(dis); ck_assert_str_eq(dis->string, "E-003"); ck_assert_str_eq(dis->next->string, "E-004"); ck_assert_ptr_null(dis->next->next); ck_assert_ptr_null(en); free_string_list(dis); dis = NULL; free_string_list(en); en = NULL; ck_assert_int_eq(SELINT_SUCCESS, parse_config(CHECKS_FILENAME, 1, &severity, &dis, &en, &cfm, &ccd)); ck_assert_ptr_nonnull(dis); ck_assert_str_eq(dis->string, "E-003"); ck_assert_str_eq(dis->next->string, "E-004"); ck_assert_ptr_null(dis->next->next); ck_assert_str_eq(en->string, "E-003"); ck_assert_ptr_null(en->next); free_string_list(dis); free_string_list(en); } END_TEST START_TEST (test_parse_config_ordering_rules) { char severity = '\0'; struct config_check_data ccd; ck_assert_int_eq(SELINT_SUCCESS, parse_config(REFPOL_ORDERING_FILENAME, 0, &severity, NULL, NULL, NULL, &ccd)); ck_assert_int_eq(ORDER_REF, ccd.order_conf); } END_TEST START_TEST (test_parse_config_ordering_requires) { char severity = '\0'; struct config_check_data ccd; // default ck_assert_int_eq(SELINT_SUCCESS, parse_config("", 0, &severity, NULL, NULL, NULL, &ccd)); ck_assert_int_eq(DECL_BOOL, ccd.order_requires[0]); ck_assert_int_eq(DECL_CLASS, ccd.order_requires[1]); ck_assert_int_eq(DECL_ROLE, ccd.order_requires[2]); ck_assert_int_eq(DECL_ATTRIBUTE_ROLE, ccd.order_requires[3]); ck_assert_int_eq(DECL_ATTRIBUTE, ccd.order_requires[4]); ck_assert_int_eq(DECL_TYPE, ccd.order_requires[5]); ck_assert_int_eq(true, ccd.ordering_requires_same_flavor); // custom ck_assert_int_eq(SELINT_SUCCESS, parse_config(ORDER_REQUIRES_FILENAME, 0, &severity, NULL, NULL, NULL, &ccd)); ck_assert_int_eq(DECL_BOOL, ccd.order_requires[0]); ck_assert_int_eq(DECL_ATTRIBUTE, ccd.order_requires[1]); ck_assert_int_eq(DECL_ATTRIBUTE_ROLE, ccd.order_requires[2]); ck_assert_int_eq(DECL_TYPE, ccd.order_requires[3]); ck_assert_int_eq(DECL_CLASS, ccd.order_requires[4]); ck_assert_int_eq(DECL_ROLE, ccd.order_requires[5]); ck_assert_int_eq(false, ccd.ordering_requires_same_flavor); } END_TEST START_TEST (test_bad_configs) { char severity = '\0'; struct string_list *dis = NULL; struct string_list *en = NULL; struct string_list *cfm = NULL; struct config_check_data ccd; ck_assert_int_eq(SELINT_CONFIG_PARSE_ERROR, parse_config(BAD_FORMAT_1, 0, &severity, &dis, &en, &cfm, &ccd)); ck_assert_ptr_null(dis); ck_assert_ptr_null(en); ck_assert_int_eq('\0', severity); ck_assert_int_eq(SELINT_CONFIG_PARSE_ERROR, parse_config(BAD_FORMAT_2, 0, &severity, &dis, &en, &cfm, &ccd)); ck_assert_ptr_null(dis); ck_assert_ptr_null(en); ck_assert_int_eq('\0', severity); ck_assert_int_eq(SELINT_CONFIG_PARSE_ERROR, parse_config(BAD_OPTION, 0, &severity, &dis, &en, &cfm, &ccd)); ck_assert_ptr_null(dis); ck_assert_ptr_null(en); ck_assert_int_eq('\0', severity); ck_assert_int_eq(SELINT_CONFIG_PARSE_ERROR, parse_config(BAD_SEVERITY, 0, &severity, &dis, &en, &cfm, &ccd)); ck_assert_ptr_null(dis); ck_assert_ptr_null(en); ck_assert_int_eq('\0', severity); ck_assert_int_eq(SELINT_CONFIG_PARSE_ERROR, parse_config(BAD_ORDER, 0, &severity, &dis, &en, &cfm, &ccd)); ck_assert_ptr_null(dis); ck_assert_ptr_null(en); ck_assert_int_eq('C', severity); } END_TEST static Suite *selint_config_suite(void) { Suite *s; TCase *tc_core; s = suite_create("SELint_config"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_parse_config_severity); tcase_add_test(tc_core, test_parse_config_checks); tcase_add_test(tc_core, test_parse_config_ordering_rules); tcase_add_test(tc_core, test_parse_config_ordering_requires); tcase_add_test(tc_core, test_bad_configs); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = selint_config_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_startup.c000066400000000000000000000113241475050262500171700ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "../src/startup.h" #include "../src/maps.h" #include "../src/selint_error.h" #include "../src/parse_functions.h" #define MODULES_CONF_PATH SAMPLE_POL_DIR "modules.conf" #define BAD_MODULES_CONF_PATH SAMPLE_POL_DIR "bad_modules.conf" #define OBJ_PERM_SETS_PATH SAMPLE_POL_DIR "obj_perm_sets.spt" #define BAD_OBJ_PERM_SETS_PATH SAMPLE_POL_DIR "bad_obj_perm_sets.spt" #define SAMPLE_AV_PATH SAMPLE_AV_DIR #define ACCESS_VECTORS_PATH SAMPLE_POL_DIR "access_vectors" START_TEST (test_load_access_vectors_kernel) { ck_assert_int_eq(load_access_vectors_kernel(SAMPLE_AV_PATH), SELINT_SUCCESS); ck_assert_int_eq(decl_map_count(DECL_CLASS), 3); ck_assert_int_eq(decl_map_count(DECL_PERM), 37); ck_assert_str_eq(look_up_in_decl_map("file", DECL_CLASS), "class"); ck_assert_str_eq(look_up_in_decl_map("append", DECL_PERM), "perm"); ck_assert_str_eq(look_up_in_decl_map("listen", DECL_PERM), "perm"); ck_assert_str_eq(look_up_in_decl_map("use", DECL_PERM), "perm"); free_all_maps(); } END_TEST START_TEST (test_load_access_vectors_source) { ck_assert_int_eq(load_access_vectors_source(ACCESS_VECTORS_PATH), SELINT_SUCCESS); ck_assert_int_eq(decl_map_count(DECL_CLASS), 8); ck_assert_int_eq(decl_map_count(DECL_PERM), 33); ck_assert_str_eq(look_up_in_decl_map("file", DECL_CLASS), "__av_file__"); ck_assert_str_eq(look_up_in_decl_map("append", DECL_PERM), "__av_file__"); ck_assert_str_eq(look_up_in_decl_map("use", DECL_PERM), "__av_file__"); free_all_maps(); } END_TEST START_TEST (test_load_modules_source) { enum selint_error res = load_modules_source(MODULES_CONF_PATH); ck_assert_int_eq(SELINT_SUCCESS, res); ck_assert_str_eq("base", look_up_in_mods_map("sysadm")); ck_assert_str_eq("module", look_up_in_mods_map("sudo")); ck_assert_str_eq("off", look_up_in_mods_map("games")); res = load_modules_source(BAD_MODULES_CONF_PATH); ck_assert_int_eq(SELINT_PARSE_ERROR, res); free_all_maps(); } END_TEST START_TEST (test_load_obj_perm_sets_source) { enum selint_error res = load_obj_perm_sets_source(OBJ_PERM_SETS_PATH); ck_assert_int_eq(SELINT_SUCCESS, res); const struct string_list *mount_fs_perms = look_up_in_permmacros_map("mount_fs_perms"); ck_assert_ptr_nonnull(mount_fs_perms); ck_assert_int_eq(1, str_in_sl("mount", mount_fs_perms)); ck_assert_int_eq(1, str_in_sl("remount", mount_fs_perms)); ck_assert_int_eq(1, str_in_sl("unmount", mount_fs_perms)); ck_assert_int_eq(1, str_in_sl("getattr", mount_fs_perms)); ck_assert_int_eq(0, str_in_sl("search", mount_fs_perms)); const struct string_list *rw_socket_perms = look_up_in_permmacros_map("rw_socket_perms"); ck_assert_ptr_nonnull(rw_socket_perms); ck_assert_int_eq(1, str_in_sl("ioctl", rw_socket_perms)); ck_assert_int_eq(1, str_in_sl("getattr", rw_socket_perms)); ck_assert_int_eq(1, str_in_sl("setattr", rw_socket_perms)); ck_assert_int_eq(1, str_in_sl("bind", rw_socket_perms)); ck_assert_int_eq(1, str_in_sl("getopt", rw_socket_perms)); ck_assert_int_eq(1, str_in_sl("shutdown", rw_socket_perms)); ck_assert_int_eq(0, str_in_sl("admin", rw_socket_perms)); const struct string_list *remount_fs_perms = look_up_in_permmacros_map("remount_fs_perms"); ck_assert_ptr_nonnull(remount_fs_perms); ck_assert_int_eq(1, str_in_sl("remount", remount_fs_perms)); ck_assert_int_eq(1, str_in_sl("getattr", remount_fs_perms)); ck_assert_ptr_null(look_up_in_permmacros_map("dir_file_class_set")); res = load_obj_perm_sets_source(BAD_OBJ_PERM_SETS_PATH); ck_assert_int_eq(SELINT_PARSE_ERROR, res); cleanup_parsing(); } END_TEST static Suite *startup_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Startup"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_load_access_vectors_kernel); tcase_add_test(tc_core, test_load_access_vectors_source); tcase_add_test(tc_core, test_load_modules_source); tcase_add_test(tc_core, test_load_obj_perm_sets_source); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = startup_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_string_list.c000066400000000000000000000135571475050262500200410ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../src/string_list.h" START_TEST (test_str_in_sl) { struct string_list *sl = calloc(1, sizeof(struct string_list)); sl->string = strdup("foo"); sl->next = calloc(1, sizeof(struct string_list)); sl->next->string = strdup("bar"); ck_assert_int_eq(1, str_in_sl("foo", sl)); ck_assert_int_eq(1, str_in_sl("bar", sl)); ck_assert_int_eq(0, str_in_sl("baz", sl)); ck_assert_int_eq(0, str_in_sl("foo", NULL)); free_string_list(sl); } END_TEST START_TEST (test_copy_string_list) { struct string_list *cur; struct string_list *sl1 = calloc(1, sizeof(struct string_list)); sl1->string = strdup("foo"); sl1->next = calloc(1, sizeof(struct string_list)); cur = sl1->next; cur->string = strdup("bar"); cur->has_incorrect_space = 1; cur->next = calloc(1,sizeof(struct string_list)); cur = cur->next; cur->string = strdup("baz"); struct string_list *sl2 = copy_string_list(sl1); ck_assert_ptr_nonnull(sl2); ck_assert_ptr_ne(sl1, sl2); ck_assert_str_eq(sl1->string, sl2->string); ck_assert_ptr_ne(sl1->string, sl2->string); ck_assert_int_eq(sl1->has_incorrect_space, sl2->has_incorrect_space); ck_assert_ptr_ne(sl1->next, sl2->next); ck_assert_ptr_nonnull(sl2->next); ck_assert_str_eq(sl1->next->string, sl2->next->string); ck_assert_ptr_ne(sl1->next->string, sl2->next->string); ck_assert_int_eq(sl1->next->has_incorrect_space, sl2->next->has_incorrect_space); ck_assert_ptr_ne(sl1->next->next, sl2->next->next); ck_assert_ptr_nonnull(sl2->next->next); ck_assert_str_eq(sl1->next->next->string, sl2->next->next->string); ck_assert_ptr_null(sl2->next->next->next); free_string_list(sl1); free_string_list(sl2); } END_TEST START_TEST (test_copy_string_list_null) { ck_assert_ptr_null(copy_string_list(NULL)); } END_TEST START_TEST (test_sl_from_str) { struct string_list *sl = sl_from_str("test"); ck_assert_ptr_nonnull(sl); ck_assert_str_eq("test", sl->string); ck_assert_int_eq(0, sl->has_incorrect_space); ck_assert_ptr_null(sl->next); free_string_list(sl); } END_TEST START_TEST (test_sl_from_strn) { struct string_list *sl = sl_from_strn("hello world", 5); ck_assert_ptr_nonnull(sl); ck_assert_str_eq("hello", sl->string); ck_assert_int_eq(0, sl->has_incorrect_space); ck_assert_ptr_null(sl->next); free_string_list(sl); sl = sl_from_strn("hello world", 0); ck_assert_ptr_nonnull(sl); ck_assert_str_eq("", sl->string); ck_assert_int_eq(0, sl->has_incorrect_space); ck_assert_ptr_null(sl->next); free_string_list(sl); } END_TEST START_TEST (test_sl_from_strs) { struct string_list *sl = sl_from_strs(2, "hello", "world"); ck_assert_ptr_nonnull(sl); ck_assert_str_eq("hello", sl->string); ck_assert_int_eq(0, sl->has_incorrect_space); ck_assert_ptr_nonnull(sl->next); ck_assert_str_eq("world", sl->next->string); ck_assert_int_eq(0, sl->next->has_incorrect_space); ck_assert_ptr_null(sl->next->next); free_string_list(sl); } END_TEST START_TEST (test_concat_string_lists) { struct string_list *res, *sl1, *sl2; ck_assert_ptr_null(concat_string_lists(NULL, NULL)); sl1 = sl_from_str("hello"); ck_assert_ptr_nonnull(sl1); sl2 = sl_from_str("world"); ck_assert_ptr_nonnull(sl2); res = concat_string_lists(NULL, sl1); ck_assert_ptr_nonnull(res); ck_assert_str_eq("hello", res->string); ck_assert_int_eq(0, res->has_incorrect_space); ck_assert_ptr_null(res->next); res = concat_string_lists(sl1, NULL); ck_assert_ptr_nonnull(res); ck_assert_str_eq("hello", res->string); ck_assert_int_eq(0, res->has_incorrect_space); ck_assert_ptr_null(res->next); res = concat_string_lists(sl1, sl2); ck_assert_ptr_nonnull(res); ck_assert_str_eq("hello", res->string); ck_assert_int_eq(0, res->has_incorrect_space); ck_assert_ptr_nonnull(res->next); ck_assert_str_eq("world", res->next->string); ck_assert_int_eq(0, res->next->has_incorrect_space); ck_assert_ptr_null(res->next->next); free_string_list(res); // frees sl1 and sl2 } END_TEST START_TEST (test_append_to_sl) { struct string_list *list = NULL; ck_assert_int_eq(SELINT_BAD_ARG, append_to_sl(list, "test")); list = sl_from_str("test1"); ck_assert_int_eq(SELINT_SUCCESS, append_to_sl(list, "test2")); ck_assert_int_eq(SELINT_SUCCESS, append_to_sl(list, "test3")); ck_assert_str_eq(list->string, "test1"); ck_assert_ptr_nonnull(list->next); ck_assert_str_eq(list->next->string, "test2"); ck_assert_ptr_nonnull(list->next->next); ck_assert_str_eq(list->next->next->string, "test3"); ck_assert_ptr_null(list->next->next->next); free_string_list(list); } END_TEST static Suite *string_list_suite(void) { Suite *s; TCase *tc_core; s = suite_create("String_list"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_str_in_sl); tcase_add_test(tc_core, test_copy_string_list); tcase_add_test(tc_core, test_copy_string_list_null); tcase_add_test(tc_core, test_sl_from_str); tcase_add_test(tc_core, test_sl_from_strn); tcase_add_test(tc_core, test_sl_from_strs); tcase_add_test(tc_core, test_concat_string_lists); tcase_add_test(tc_core, test_append_to_sl); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = string_list_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_te_checks.c000066400000000000000000000204511475050262500174170ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "test_utils.h" #include "../src/te_checks.h" #include "../src/check_hooks.h" #include "../src/maps.h" START_TEST (test_check_te_order) { struct check_data *cd = calloc(1, sizeof(struct check_data)); cd->flavor = FILE_TE_FILE; struct config_check_data *ccd = calloc(1, sizeof(struct config_check_data)); ccd->order_conf = ORDER_REF; cd->config_check_data = ccd; struct policy_node *head = calloc(1, sizeof(struct policy_node)); head->flavor = NODE_TE_FILE; head->next = calloc(1, sizeof(struct policy_node)); struct policy_node *cur = head->next; cur->flavor = NODE_DECL; cur->data.d_data = calloc(1, sizeof(struct declaration_data)); cur->data.d_data->flavor = DECL_TYPE; cur->data.d_data->name = strdup("foo_t"); cur->next = calloc(1, sizeof(struct policy_node)); cur = cur->next; cur->flavor = NODE_IF_CALL; cur->data.ic_data = calloc(1, sizeof(struct if_call_data)); cur->data.ic_data->name = strdup("domain_type"); cur->data.ic_data->args = calloc(1, sizeof(struct string_list)); cur->data.ic_data->args->string = strdup("foo_t"); mark_transform_if("domain_type"); cur->next = calloc(1, sizeof(struct policy_node)); cur = cur->next; cur->flavor = NODE_AV_RULE; cur->data.av_data = make_example_av_rule(); cur = head; while (cur) { ck_assert_ptr_null(check_te_order(cd, cur)); cur = dfs_next(cur); } struct policy_node *cleanup = calloc(1, sizeof(struct policy_node)); cleanup->flavor = NODE_CLEANUP; ck_assert_ptr_null(check_te_order(cd, cleanup)); free(ccd); free(cd); free_policy_node(head); free_policy_node(cleanup); free_all_maps(); } END_TEST START_TEST (test_check_require_block) { struct policy_node *cur = calloc(1, sizeof(struct policy_node)); cur->flavor = NODE_REQUIRE; struct check_data *cd = calloc(1, sizeof(struct check_data)); cd->flavor = FILE_IF_FILE; ck_assert_ptr_null(check_require_block(cd, cur)); cd->flavor = FILE_FC_FILE; ck_assert_ptr_null(check_require_block(cd, cur)); cd->flavor = FILE_TE_FILE; cur->first_child = calloc(1, sizeof(struct policy_node)); cur->first_child->flavor = NODE_DECL; cur->first_child->data.d_data = calloc(1, sizeof(struct declaration_data)); cur->first_child->data.d_data->flavor = DECL_TYPE; struct check_result *res = check_require_block(cd, cur); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'S'); ck_assert_int_eq(res->check_id, S_ID_REQUIRE); free_check_result(res); cur->flavor = NODE_GEN_REQ; res = check_require_block(cd, cur); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'S'); ck_assert_int_eq(res->check_id, S_ID_REQUIRE); free_check_result(res); free(cd); free_policy_node(cur); } END_TEST START_TEST (test_check_useless_semicolon) { struct policy_node *cur = calloc(1, sizeof(struct policy_node)); cur->flavor = NODE_SEMICOLON; struct check_data *cd = calloc(1, sizeof(struct check_data)); struct check_result *res = check_useless_semicolon(cd, cur); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'S'); ck_assert_int_eq(res->check_id, S_ID_SEMICOLON); free_check_result(res); free(cd); free_policy_node(cur); } END_TEST START_TEST (test_check_no_explicit_declaration) { struct policy_node *cur = calloc(1, sizeof(struct policy_node)); struct check_data *cd = calloc(1, sizeof(struct check_data)); cur->flavor = NODE_AV_RULE; cur->data.av_data = make_example_av_rule(); cd->flavor = FILE_IF_FILE; cd->mod_name = strdup("foo"); ck_assert_ptr_null(check_no_explicit_declaration(cd, cur)); cd->flavor = FILE_TE_FILE; // If the type isn't found, we don't do anything ck_assert_ptr_null(check_no_explicit_declaration(cd, cur)); insert_into_decl_map("foo_t", "foo", DECL_TYPE); insert_into_decl_map("other_t", "other", DECL_TYPE); ck_assert_ptr_null(check_no_explicit_declaration(cd, cur)); insert_into_decl_map("bar_t", "bar", DECL_TYPE); struct check_result *res = check_no_explicit_declaration(cd, cur); ck_assert_ptr_nonnull(res); ck_assert_int_eq(W_ID_NO_EXPLICIT_DECL, res->check_id); free_check_result(res); // Require block cur->prev = calloc(1, sizeof(struct policy_node)); cur->prev->next = cur; cur = cur->prev; cur->flavor = NODE_REQUIRE; union node_data nd; nd.d_data = NULL; ck_assert_int_eq(SELINT_SUCCESS, insert_policy_node_child(cur, NODE_START_BLOCK, nd, 0)); nd.d_data = calloc(1, sizeof(struct declaration_data)); nd.d_data->flavor = DECL_TYPE; nd.d_data->name = strdup("bar_t"); ck_assert_int_eq(SELINT_SUCCESS, insert_policy_node_child(cur, NODE_DECL, nd, 0)); ck_assert_ptr_null(check_no_explicit_declaration(cd, cur->next)); cur->flavor = NODE_GEN_REQ; ck_assert_ptr_null(check_no_explicit_declaration(cd, cur->next)); free(cur->first_child->next->data.d_data->name); cur->first_child->next->data.d_data->name = strdup("baz_t"); res = check_no_explicit_declaration(cd, cur->next); ck_assert_ptr_nonnull(res); ck_assert_int_eq(W_ID_NO_EXPLICIT_DECL, res->check_id); free_check_result(res); free_all_maps(); free(cd->mod_name); free(cd); free_policy_node(cur); } END_TEST START_TEST (test_check_module_if_call_in_optional) { struct check_result *res; char *foo_read_str = strdup("foo_read"); char *bar_read_str = strdup("bar_read"); struct policy_node *cur = calloc(1, sizeof(struct policy_node)); cur->flavor = NODE_IF_CALL; struct if_call_data *ic_data = calloc(1, sizeof(struct if_call_data)); cur->data.ic_data = ic_data; ic_data->name = foo_read_str; struct check_data *cd = calloc(1, sizeof(struct check_data)); cd->mod_name = strdup("baz"); insert_into_ifs_map("foo_read", "foo"); insert_into_mods_map("foo", "module"); res = check_module_if_call_in_optional(cd, cur); ck_assert_ptr_nonnull(res); free_check_result(res); ic_data->name = bar_read_str; insert_into_ifs_map("bar_read", "bar"); insert_into_mods_map("bar", "base"); res = check_module_if_call_in_optional(cd, cur); ck_assert_ptr_null(res); cur->parent = calloc(1, sizeof(struct policy_node)); cur->parent->flavor = NODE_OPTIONAL_POLICY; cur->parent->first_child = cur; res = check_module_if_call_in_optional(cd, cur); ck_assert_ptr_null(res); ic_data->name = foo_read_str; res = check_module_if_call_in_optional(cd, cur); ck_assert_ptr_null(res); free(bar_read_str); free(cd->mod_name); free(cd); free_all_maps(); free_policy_node(cur->parent); } END_TEST START_TEST (test_check_attribute_interface_nameclash) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_DECL; node->data.d_data = calloc(1, sizeof(struct declaration_data)); node->data.d_data->flavor = DECL_ATTRIBUTE; node->data.d_data->name = strdup("foo"); ck_assert_ptr_null(check_declaration_interface_nameclash(NULL, node)); insert_into_ifs_map("foo", "bar"); struct check_result *res = check_declaration_interface_nameclash(NULL, node); ck_assert_ptr_nonnull(res); ck_assert_int_eq(res->severity, 'E'); ck_assert_int_eq(res->check_id, E_ID_DECL_IF_CLASH); free_check_result(res); free_policy_node(node); free_all_maps(); } END_TEST static Suite *te_checks_suite(void) { Suite *s; TCase *tc_core; s = suite_create("TE_Checks"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_check_te_order); tcase_add_test(tc_core, test_check_require_block); tcase_add_test(tc_core, test_check_useless_semicolon); tcase_add_test(tc_core, test_check_no_explicit_declaration); tcase_add_test(tc_core, test_check_module_if_call_in_optional); tcase_add_test(tc_core, test_check_attribute_interface_nameclash); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = te_checks_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_template.c000066400000000000000000000202121475050262500172750ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "../src/parse_functions.h" #include "../src/template.h" #include "../src/parse.h" #include "../src/maps.h" #define POLICIES_DIR SAMPLE_POL_DIR #define NESTED_IF_FILENAME POLICIES_DIR "nested_templates.if" #define DECLARING_IF_FILENAME POLICIES_DIR "declaring_template.if" #define DECLARING_TE_FILENAME POLICIES_DIR "declaring_template.te" START_TEST (test_replace_m4) { const char *orig1 = "$1_t"; struct string_list *args = calloc(1,sizeof(struct string_list)); args->string = strdup("foo"); args->next = calloc(1,sizeof(struct string_list)); args->next->string = strdup("bar"); args->next->next = NULL; char *res = replace_m4(orig1, args); ck_assert_ptr_nonnull(res); ck_assert_str_eq("foo_t", res); free(res); const char *orig2 = "$2"; res = replace_m4(orig2, args); ck_assert_ptr_nonnull(res); ck_assert_str_eq("bar", res); free(res); const char *orig3 = "test_$1_test"; res = replace_m4(orig3, args); ck_assert_ptr_nonnull(res); ck_assert_str_eq("test_foo_test", res); free(res); const char *orig4 = "test$2$1"; res = replace_m4(orig4, args); ck_assert_ptr_nonnull(res); ck_assert_str_eq("testbarfoo", res); free(res); free_string_list(args); } END_TEST START_TEST (test_replace_m4_too_few_args) { struct string_list *args = calloc(1,sizeof(struct string_list)); args->string = strdup("foo"); args->next = calloc(1,sizeof(struct string_list)); args->next->string = strdup("bar"); args->next->next = NULL; const char *orig = "$3_t"; char *ret = replace_m4(orig, args); ck_assert_ptr_nonnull(ret); ck_assert_str_eq("_t", ret); free_string_list(args); free(ret); } END_TEST START_TEST (test_replace_m4_nothing_to_replace) { struct string_list *args = calloc(1, sizeof(struct string_list)); args->string = strdup("foo"); const char *orig = "bar_t"; char *res = replace_m4(orig, args); ck_assert_ptr_nonnull(res); ck_assert_str_eq("bar_t", res); free(res); free_string_list(args); } END_TEST START_TEST (test_replace_m4_bad_dollar_sign) { struct string_list *args = calloc(1, sizeof(struct string_list)); args->string = strdup("foo"); const char *orig = "$string"; ck_assert_ptr_null(replace_m4(orig, args)); free_string_list(args); } END_TEST START_TEST (test_replace_m4_list) { struct string_list *caller_args = calloc(1,sizeof(struct string_list)); caller_args->string = strdup("foo"); caller_args->next = calloc(1,sizeof(struct string_list)); caller_args->next->string = strdup("bar"); caller_args->next->next = NULL; struct string_list *called_args = calloc(1,sizeof(struct string_list)); called_args->string = strdup("$2"); called_args->next = calloc(1,sizeof(struct string_list)); called_args->next->string = strdup("$1"); called_args->next->next = NULL; struct string_list *ret = replace_m4_list(caller_args, called_args); ck_assert_ptr_nonnull(ret); ck_assert_str_eq(ret->string, "bar"); ck_assert_ptr_nonnull(ret->next); ck_assert_str_eq(ret->next->string, "foo"); ck_assert_ptr_null(ret->next->next); free_string_list(caller_args); free_string_list(called_args); free_string_list(ret); } END_TEST START_TEST (test_replace_m4_list_too_few_args) { struct string_list *caller_args = calloc(1,sizeof(struct string_list)); caller_args->string = strdup("foo"); struct string_list *called_args = calloc(1,sizeof(struct string_list)); called_args->string = strdup("$5"); struct string_list *ret = replace_m4_list(caller_args, called_args); ck_assert_ptr_nonnull(ret); ck_assert_ptr_nonnull(ret->string); ck_assert_str_eq("", ret->string); free_string_list(caller_args); free_string_list(called_args); free_string_list(ret); } END_TEST START_TEST (test_nested_template_declarations) { set_current_module_name("nested"); FILE *f = fopen(NESTED_IF_FILENAME, "r"); ck_assert_ptr_nonnull(f); struct policy_node *ast = yyparse_wrapper(f, NESTED_IF_FILENAME, NODE_IF_FILE); ck_assert_ptr_nonnull(ast); fclose(f); struct string_list *called_args = calloc(1,sizeof(struct string_list)); called_args->string = strdup("first"); called_args->next = calloc(1,sizeof(struct string_list)); called_args->next->string = strdup("second"); called_args->next->next = calloc(1,sizeof(struct string_list)); called_args->next->next->string = strdup("third"); called_args->next->next->next = NULL; ck_assert_int_eq(SELINT_SUCCESS, add_template_declarations("outer", called_args, NULL, "nested_interfaces")); ck_assert_str_eq("nested_interfaces", look_up_in_decl_map("first_t", DECL_TYPE)); ck_assert_str_eq("nested_interfaces", look_up_in_decl_map("third_foo_t", DECL_TYPE)); ck_assert_str_eq("nested_interfaces", look_up_in_decl_map("second_bar_t", DECL_TYPE)); ck_assert_ptr_null(look_up_in_decl_map("second_foo_t", DECL_TYPE)); ck_assert_ptr_null(look_up_in_decl_map("third_bar_t", DECL_TYPE)); free_string_list(called_args); free_policy_node(ast); cleanup_parsing(); } END_TEST START_TEST (test_declaring_template) { // setup set_current_module_name("declaring_template_if"); FILE *f_if = fopen(DECLARING_IF_FILENAME, "r"); ck_assert_ptr_nonnull(f_if); struct policy_node *ast_if = yyparse_wrapper(f_if, DECLARING_IF_FILENAME, NODE_IF_FILE); ck_assert_ptr_nonnull(ast_if); fclose(f_if); set_current_module_name("declaring_template_te"); FILE *f_te = fopen(DECLARING_TE_FILENAME, "r"); ck_assert_ptr_nonnull(f_te); struct policy_node *ast_te = yyparse_wrapper(f_te, DECLARING_TE_FILENAME, NODE_TE_FILE); ck_assert_ptr_nonnull(ast_te); fclose(f_te); // checks const char *mod_name; ck_assert_uint_eq(4, decl_map_count(DECL_TYPE)); mod_name = look_up_in_decl_map("prefix_foo_suffix", DECL_TYPE); ck_assert_str_eq("declaring_template_te", mod_name); mod_name = look_up_in_decl_map("bar_t", DECL_TYPE); ck_assert_str_eq("declaring_template_te", mod_name); mod_name = look_up_in_decl_map("prefix_good_suffix", DECL_TYPE); ck_assert_str_eq("declaring_template_te", mod_name); mod_name = look_up_in_decl_map("morning_t", DECL_TYPE); ck_assert_str_eq("declaring_template_te", mod_name); // these are called via an interface so it isn't declared mod_name = look_up_in_decl_map("prefix_hello_suffix", DECL_TYPE); ck_assert_ptr_null(mod_name); mod_name = look_up_in_decl_map("world_t", DECL_TYPE); ck_assert_ptr_null(mod_name); ck_assert_uint_eq(2, decl_map_count(DECL_ROLE)); mod_name = look_up_in_decl_map("bar_r", DECL_ROLE); ck_assert_str_eq("declaring_template_te", mod_name); mod_name = look_up_in_decl_map("morning_r", DECL_ROLE); ck_assert_str_eq("declaring_template_te", mod_name); // this is called via an interface so it isn't declared mod_name = look_up_in_decl_map("world_t", DECL_TYPE); ck_assert_ptr_null(mod_name); // cleanup free_policy_node(ast_te); free_policy_node(ast_if); cleanup_parsing(); } END_TEST static Suite *template_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Template"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_replace_m4); tcase_add_test(tc_core, test_replace_m4_too_few_args); tcase_add_test(tc_core, test_replace_m4_nothing_to_replace); tcase_add_test(tc_core, test_replace_m4_bad_dollar_sign); tcase_add_test(tc_core, test_replace_m4_list); tcase_add_test(tc_core, test_replace_m4_list_too_few_args); tcase_add_test(tc_core, test_nested_template_declarations); tcase_add_test(tc_core, test_declaring_template); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = template_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/check_tree.c000066400000000000000000000205451475050262500164320ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "test_utils.h" #include "../src/tree.h" #include "../src/maps.h" START_TEST (test_insert_policy_node_child) { struct policy_node parent_node; parent_node.parent = NULL; parent_node.next = NULL; parent_node.prev = NULL; parent_node.first_child = NULL; parent_node.flavor = NODE_TE_FILE; parent_node.data.str = NULL; union node_data nd; nd.av_data = make_example_av_rule(); ck_assert_int_eq(SELINT_SUCCESS, insert_policy_node_child(&parent_node, NODE_AV_RULE, nd, 1234)); ck_assert_ptr_null(parent_node.next); ck_assert_ptr_nonnull(parent_node.first_child); ck_assert_ptr_eq(parent_node.first_child->data.av_data, nd.av_data); ck_assert_int_eq(parent_node.first_child->flavor, NODE_AV_RULE); ck_assert_int_eq(parent_node.first_child->lineno, 1234); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(parent_node.first_child)); } END_TEST START_TEST (test_insert_policy_node_next) { struct policy_node prev_node; prev_node.parent = NULL; prev_node.next = NULL; prev_node.prev = NULL; prev_node.first_child = NULL; prev_node.flavor = NODE_TE_FILE; prev_node.data.str = NULL; union node_data nd; nd.av_data = make_example_av_rule(); ck_assert_int_eq(SELINT_SUCCESS, insert_policy_node_next(&prev_node, NODE_AV_RULE, nd, 1234)); ck_assert_ptr_null(prev_node.first_child); ck_assert_ptr_nonnull(prev_node.next); ck_assert_ptr_eq(prev_node.next->data.av_data, nd.av_data); ck_assert_int_eq(prev_node.next->flavor, NODE_AV_RULE); ck_assert_int_eq(prev_node.next->lineno, 1234); ck_assert_int_eq(SELINT_SUCCESS, free_policy_node(prev_node.next)); } END_TEST START_TEST (test_is_template_call) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); ck_assert_int_eq(0, is_template_call(node)); struct if_call_data *data = calloc(1, sizeof(struct if_call_data)); data->name = strdup("foo"); node->data.ic_data = data; ck_assert_int_eq(0, is_template_call(node)); node->flavor = NODE_IF_CALL; ck_assert_int_eq(0, is_template_call(node)); insert_call_into_template_map("foo", data); ck_assert_int_eq(1, is_template_call(node)); free_policy_node(node); free_all_maps(); } END_TEST START_TEST (test_get_types_in_node_av) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_AV_RULE; node->data.av_data = make_example_av_rule(); struct name_list *out = get_names_in_node(node); struct name_list *cur = out; ck_assert_ptr_nonnull(cur); ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_1); ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_2); ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_3); ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); ck_assert_str_eq(cur->data->name, "file"); ck_assert_int_eq(cur->data->flavor, NAME_CLASS); ck_assert_ptr_null(cur->next); free_name_list(out); free_policy_node(node); } END_TEST START_TEST (test_get_types_in_node_tt) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_TT_RULE; node->data.tt_data = calloc(1, sizeof(struct type_transition_data)); struct type_transition_data *tt_data = (struct type_transition_data *)node->data.tt_data; tt_data->sources = calloc(1, sizeof(struct string_list)); tt_data->sources->string = strdup(EXAMPLE_TYPE_3); tt_data->targets = calloc(1, sizeof(struct string_list)); tt_data->targets->string = strdup(EXAMPLE_TYPE_2); tt_data->default_type = strdup(EXAMPLE_TYPE_1); struct name_list *out = get_names_in_node(node); struct name_list *cur = out; ck_assert_ptr_nonnull(cur); ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_3); ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_2); ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_1); ck_assert_int_eq(cur->data->flavor, NAME_TYPE); ck_assert_ptr_null(cur->next); free_name_list(out); free_policy_node(node); } END_TEST START_TEST (test_get_types_in_node_dd) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_DECL; node->data.d_data = calloc(1, sizeof(struct declaration_data)); struct declaration_data *d_data = (struct declaration_data *)node->data.d_data; d_data->name = strdup(EXAMPLE_TYPE_2); struct name_list *out = get_names_in_node(node); ck_assert_ptr_nonnull(out); ck_assert_str_eq(out->data->name, EXAMPLE_TYPE_2); ck_assert_int_eq(out->data->flavor, NAME_TYPE); ck_assert_ptr_null(out->next); free_name_list(out); free_policy_node(node); } END_TEST START_TEST (test_get_types_in_node_if_call) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_IF_CALL; node->data.ic_data = calloc(1, sizeof(struct if_call_data)); struct if_call_data *if_data = (struct if_call_data *)node->data.ic_data; if_data->name = strdup("foo_read"); if_data->args = calloc(1, sizeof(struct string_list)); if_data->args->string = strdup("bar_t"); if_data->args->next = calloc(1, sizeof(struct string_list)); if_data->args->next->string = strdup("baz_t"); struct name_list *out = get_names_in_node(node); ck_assert_ptr_nonnull(out); ck_assert_str_eq(out->data->name, "bar_t"); ck_assert_int_eq(out->data->flavor, NAME_UNKNOWN); ck_assert_str_eq(out->next->data->name, "baz_t"); ck_assert_int_eq(out->next->data->flavor, NAME_UNKNOWN); ck_assert_ptr_null(out->next->next); free_name_list(out); free_policy_node(node); } END_TEST START_TEST (test_get_types_in_node_no_types) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_ERROR; ck_assert_ptr_null(get_names_in_node(node)); free_policy_node(node); } END_TEST START_TEST (test_get_types_in_node_exclusion) { struct policy_node *node = calloc(1, sizeof(struct policy_node)); node->flavor = NODE_AV_RULE; node->data.av_data = calloc(1, sizeof(struct av_rule_data)); node->data.av_data->sources = calloc(1, sizeof(struct string_list)); node->data.av_data->sources->string = strdup("domain"); node->data.av_data->sources->next = calloc(1, sizeof(struct string_list)); node->data.av_data->sources->next->string = strdup("-init_t"); struct name_list *out = get_names_in_node(node); ck_assert_ptr_nonnull(out); ck_assert_str_eq(out->data->name, "domain"); ck_assert_int_eq(out->data->flavor, NAME_TYPE_OR_ATTRIBUTE); ck_assert_str_eq(out->next->data->name, "init_t"); // Strip "-" ck_assert_int_eq(out->next->data->flavor, NAME_TYPE_OR_ATTRIBUTE); ck_assert_ptr_null(out->next->next); free_name_list(out); free_policy_node(node); } END_TEST static Suite *tree_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Tree"); tc_core = tcase_create("Core"); tcase_add_test(tc_core, test_insert_policy_node_child); tcase_add_test(tc_core, test_insert_policy_node_next); tcase_add_test(tc_core, test_is_template_call); tcase_add_test(tc_core, test_get_types_in_node_av); tcase_add_test(tc_core, test_get_types_in_node_tt); tcase_add_test(tc_core, test_get_types_in_node_dd); tcase_add_test(tc_core, test_get_types_in_node_if_call); tcase_add_test(tc_core, test_get_types_in_node_no_types); tcase_add_test(tc_core, test_get_types_in_node_exclusion); suite_add_tcase(s, tc_core); return s; } int main(void) { int number_failed = 0; Suite *s; SRunner *sr; s = tree_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0)? 0 : -1; } selint-1.5.1/tests/functional/000077500000000000000000000000001475050262500163265ustar00rootroot00000000000000selint-1.5.1/tests/functional/configs/000077500000000000000000000000001475050262500177565ustar00rootroot00000000000000selint-1.5.1/tests/functional/configs/bad_ids.conf000066400000000000000000000001111475050262500222030ustar00rootroot00000000000000disable = { E-003, E-004, foo } enable_source = { E-003, E-004, X-999 } selint-1.5.1/tests/functional/configs/broken.conf000066400000000000000000000000221475050262500220770ustar00rootroot00000000000000asdfasfdj=asdfasf selint-1.5.1/tests/functional/configs/default.conf000066400000000000000000000033361475050262500222560ustar00rootroot00000000000000# This is the configuration file for SELint. The global configuration file # is the default if no other configuration is provided, but can be overridden # by user specific and project specific configurations, or a configuration file # can be provided on the command line. # Set the severity level to report. Options are "convention", "style", # "warning", "error" and "fatal". SELint will report all errors at or above # the selected level severity = "convention" # Check enabling works as follows each step may override the one prior: # 1. All checks at or above the set severity level are enabled by default # 2. Checks may be disabled using the "disable" option below # 3. Checks may be enabled in either normal or source modes using the "enable_normal" and "enable_source" options below # 4. Checks may be disabled on the command line using the -d option # 5. Checks may be enabled on the command line using the -e option # Uncomment and modify to disable selected checks. This can be overridden on # the command line disable = { E-003, E-004 } # enable description #enable_normal = { S-002, E-002 } enable_source = { E-003, E-004 } # Modules.conf location. If you are running SELint in "source mode", you need # to supply a modules.conf file in order to run all checks. SELint will look # in the default location of policy/modules.conf from the directory where it # is run. To use a different path specify it here. #modules_conf_path = policy/modules.conf # Users and roles are often not declared in te files like other policy # constructs. Use the below options to specify valid users and roles that # SELint should not report as invalid even if they are not found in the # policy assume_users = { system_u } assume_roles = { object_r } selint-1.5.1/tests/functional/configs/empty.conf000066400000000000000000000034631475050262500217710ustar00rootroot00000000000000# This is the configuration file for SELint. The global configuration file # is the default if no other configuration is provided, but can be overridden # by user specific and project specific configurations, or a configuration file # can be provided on the command line. # Set the severity level to report. Options are "convention", "style", # "warning", "error" and "fatal". SELint will report all errors at or above # the selected level severity = "convention" # Check enabling works as follows each step may override the one prior: # 1. All checks at or above the set severity level are enabled by default # 2. Checks may be disabled using the "disable" option below # 3. Checks may be enabled in either normal or source modes using the "enable_normal" and "enable_source" options below # 4. Checks may be disabled on the command line using the -d option # 5. Checks may be enabled on the command line using the -e option # Uncomment and modify to disable selected checks. This can be overridden on # the command line disable = { C-001, C-002, S-001, S-002, W-001, W-002, W-003, W-004, W-005, E-001, E-002, E-003, E-004, E-005 } # enable description #enable_normal = { S-002, E-002 } #enable_source = { E-003, E-004 } # Modules.conf location. If you are running SELint in "source mode", you need # to supply a modules.conf file in order to run all checks. SELint will look # in the default location of policy/modules.conf from the directory where it # is run. To use a different path specify it here. #modules_conf_path = policy/modules.conf # Users and roles are often not declared in te files like other policy # constructs. Use the below options to specify valid users and roles that # SELint should not report as invalid even if they are not found in the # policy assume_users = { system_u } assume_roles = { object_r } selint-1.5.1/tests/functional/configs/fc_macros.conf000066400000000000000000000000471475050262500225620ustar00rootroot00000000000000custom_fc_macros = { my_custom_macro } selint-1.5.1/tests/functional/configs/order_lax.conf000066400000000000000000000034001475050262500226010ustar00rootroot00000000000000# This is the configuration file for SELint. The global configuration file # is the default if no other configuration is provided, but can be overridden # by user specific and project specific configurations, or a configuration file # can be provided on the command line. # Set the severity level to report. Options are "convention", "style", # "warning", "error" and "fatal". SELint will report all errors at or above # the selected level severity = "convention" # Check enabling works as follows each step may override the one prior: # 1. All checks at or above the set severity level are enabled by default # 2. Checks may be disabled using the "disable" option below # 3. Checks may be enabled in either normal or source modes using the "enable_normal" and "enable_source" options below # 4. Checks may be disabled on the command line using the -d option # 5. Checks may be enabled on the command line using the -e option # Uncomment and modify to disable selected checks. This can be overridden on # the command line disable = { E-003, E-004 } # enable description #enable_normal = { S-002, E-002 } enable_source = { E-003, E-004 } # Modules.conf location. If you are running SELint in "source mode", you need # to supply a modules.conf file in order to run all checks. SELint will look # in the default location of policy/modules.conf from the directory where it # is run. To use a different path specify it here. #modules_conf_path = policy/modules.conf # Users and roles are often not declared in te files like other policy # constructs. Use the below options to specify valid users and roles that # SELint should not report as invalid even if they are not found in the # policy assume_users = { system_u } assume_roles = { object_r } ordering_rules = "refpolicy-lax" selint-1.5.1/tests/functional/configs/order_ref.conf000066400000000000000000000033741475050262500226030ustar00rootroot00000000000000# This is the configuration file for SELint. The global configuration file # is the default if no other configuration is provided, but can be overridden # by user specific and project specific configurations, or a configuration file # can be provided on the command line. # Set the severity level to report. Options are "convention", "style", # "warning", "error" and "fatal". SELint will report all errors at or above # the selected level severity = "convention" # Check enabling works as follows each step may override the one prior: # 1. All checks at or above the set severity level are enabled by default # 2. Checks may be disabled using the "disable" option below # 3. Checks may be enabled in either normal or source modes using the "enable_normal" and "enable_source" options below # 4. Checks may be disabled on the command line using the -d option # 5. Checks may be enabled on the command line using the -e option # Uncomment and modify to disable selected checks. This can be overridden on # the command line disable = { E-003, E-004 } # enable description #enable_normal = { S-002, E-002 } enable_source = { E-003, E-004 } # Modules.conf location. If you are running SELint in "source mode", you need # to supply a modules.conf file in order to run all checks. SELint will look # in the default location of policy/modules.conf from the directory where it # is run. To use a different path specify it here. #modules_conf_path = policy/modules.conf # Users and roles are often not declared in te files like other policy # constructs. Use the below options to specify valid users and roles that # SELint should not report as invalid even if they are not found in the # policy assume_users = { system_u } assume_roles = { object_r } ordering_rules = "refpolicy" selint-1.5.1/tests/functional/end-to-end.bats000066400000000000000000000342371475050262500211440ustar00rootroot00000000000000#!/usr/bin/env/bats # Copyright 2019 Tresys Technology, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. SELINT_PATH=../../src/selint do_test() { local CHECK_ID=$1 local FILENAME=$2 local EXPECT=$3 local ARGS=$4 run ${SELINT_PATH} -s -c tmp.conf ${ARGS} ./policies/check_triggers/${FILENAME} ./policies/check_triggers/modules.conf ./policies/check_triggers/obj_perm_sets.spt ./policies/check_triggers/access_vectors ./policies/check_triggers/security_classes echo $output [ "$status" -eq 0 ] count=$(echo ${output} | grep -o ${CHECK_ID} | wc -l) echo "Status: $status, Count: $count (expected ${EXPECT})" [ "$count" -eq ${EXPECT} ] } test_one_check_expect() { local CHECK_ID=$1 local FILENAME=$2 local EXPECT=$3 echo "disable = { $CHECK_ID } " > tmp.conf # E-010 echo "custom_te_simple_macros = { ignored_bare_word }" >> tmp.conf do_test ${CHECK_ID} ${FILENAME} 0 echo "enable_source = { $CHECK_ID }" >> tmp.conf do_test ${CHECK_ID} ${FILENAME} ${EXPECT} do_test ${CHECK_ID} ${FILENAME} 0 "-d $CHECK_ID" do_test ${CHECK_ID} ${FILENAME} ${EXPECT} "-e $CHECK_ID" do_test ${CHECK_ID} ${FILENAME} ${EXPECT} "-e $CHECK_ID -d $CHECK_ID" rm tmp.conf } test_ordering() { local CHECK_DIR="./policies/check_triggers/C-001/" local FILENAME_PREFIX=$1 for ORDER_CONF in "ref" "lax" do echo "Checking ${FILENAME_PREFIX} in order ${ORDER_CONF}" run ${SELINT_PATH} -rs -c configs/order_${ORDER_CONF}.conf -e C-001 -E --context=${CHECK_DIR}interfaces ${CHECK_DIR}${FILENAME_PREFIX}.te ./policies/check_triggers/modules.conf echo ${output} while read p; do echo "Checking for $p" count=$(echo ${output} | grep -o ${p} | wc -l) [ "$count" -eq "1" ] done < "${CHECK_DIR}/${FILENAME_PREFIX}.expect.${ORDER_CONF}" local EXPECT_COUNT=$(cat ${CHECK_DIR}/${FILENAME_PREFIX}.expect.${ORDER_CONF} | wc -l) count=$(echo ${output} | grep -o C-001 | wc -l) echo "Expecting: ${EXPECT_COUNT}, got: $count" [ "$count" -eq "${EXPECT_COUNT}" ] done } test_one_check() { test_one_check_expect $1 $2 1 } test_parse_error_run() { local USE_VALGRIND=$1 test_parse_error_impl $USE_VALGRIND test1.te test1.output test_parse_error_impl $USE_VALGRIND test2.te test2.output printf "#comment\n policy_module( test2, \n \n\n 0.0.1 \n ) \n\n" > ./policies/parse_errors/test3_tmp.if test_parse_error_impl $USE_VALGRIND test3_tmp.if test3.output test_parse_error_impl $USE_VALGRIND test4.te test4.output printf "policy_module(test5, 0.0.1)\r\n\r\n# comment\r\nallow \r\n source\r\ntarget \r\n clas\r\n perm\r\n; # comment\r\n\r\n" > ./policies/parse_errors/test5_tmp.te test_parse_error_impl $USE_VALGRIND test5_tmp.te test5.output printf "#comment\r\n policy_module( test2, \r\n \r\n\r\n 0.0.1 \r\n ) \r\n\r\n" > ./policies/parse_errors/test6_tmp.if test_parse_error_impl $USE_VALGRIND test6_tmp.if test6.output test_parse_error_impl $USE_VALGRIND test7.if test7.output test_parse_error_impl $USE_VALGRIND test8.te test8.output test_parse_error_impl $USE_VALGRIND test9.te test9.output } test_parse_error_impl() { local USE_VALGRIND=$1 local SOURCE_FILENAME=$2 local OUTPUT_FILENAME=$3 if [ $USE_VALGRIND -eq 1 ]; then run valgrind --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=23 ${SELINT_PATH} -c configs/default.conf ./policies/parse_errors/${SOURCE_FILENAME} [ "$status" -eq 70 ] else run ${SELINT_PATH} -c configs/default.conf ./policies/parse_errors/${SOURCE_FILENAME} echo ${output} [ "$status" -eq 70 ] local EXPECTED_OUTPUT=$(cat ./policies/parse_errors/${OUTPUT_FILENAME}) echo ${EXPECTED_OUTPUT} # compatibility for different bison versions and ignore NOTE output local NORMALIZED_OUTPUT=$(grep -vE '^Note: ' <<< "${output/ \$end/ end of file}") echo ${NORMALIZED_OUTPUT} [ "${NORMALIZED_OUTPUT}" == "${EXPECTED_OUTPUT}" ] fi } test_report_format_impl() { local OPTIONS=$1 local SOURCE_FILENAME=$2 local OUTPUT_FILENAME=$3 run ${SELINT_PATH} ${OPTIONS} -c configs/default.conf ./policies/report_format/${SOURCE_FILENAME} echo ${output} [ "$status" -eq 0 ] local EXPECTED_OUTPUT=$(cat ./policies/report_format/${OUTPUT_FILENAME}) echo ${EXPECTED_OUTPUT} # compatibility for different bison versions and ignore NOTE output local NORMALIZED_OUTPUT=$(grep -vE '^Note: ' <<< "${output/ \$end/ end of file}") echo ${NORMALIZED_OUTPUT} [ "${NORMALIZED_OUTPUT}" == "${EXPECTED_OUTPUT}" ] } @test "X-001" { test_one_check "X-001" "x01.*" } @test "X-002" { test_one_check_expect "X-002" "x02.te" 5 } @test "C-001" { test_ordering "simple" test_ordering "self_macro" test_ordering "interleaved" test_ordering "interleaved2" test_ordering "optional" test_ordering "optional_optional" test_ordering "role_ifs" test_ordering "types_in_requires" test_ordering "kernel_module_first" test_ordering "if_in_optional" } @test "C-004" { test_one_check "C-004" "c04.if" } @test "C-005" { test_one_check_expect "C-005" "c05.te" 2 test_one_check "C-005" "c05.if" } @test "C-006" { test_one_check_expect "C-006" "c06.pass.if" 0 test_one_check_expect "C-006" "c06.warn.if" 5 } @test "C-007" { test_one_check "C-007" "c07.te" test_one_check "C-007" "c07.if" } @test "C-008" { test_one_check "C-008" "c08*.te" } @test "S-001" { test_one_check "S-001" "s01.te" } @test "S-002" { test_one_check "S-002" "s02*" } @test "S-003" { test_one_check "S-003" "s03.te" } @test "S-004" { test_one_check "S-004" "s04.*" } @test "S-005" { test_one_check "S-005" "s05.if" } @test "S-006" { test_one_check "S-006" "s06.te" } @test "S-007" { test_one_check "S-007" "s07.fc" } @test "S-008" { test_one_check "S-008" "s08.if" } @test "S-009" { test_one_check_expect "S-009" "s09.pass.te" 0 test_one_check_expect "S-009" "s09.warn.te" 6 } @test "W-001" { test_one_check_expect "W-001" "w01*" 6 } @test "W-002" { test_one_check_expect "W-002" "w02.*" 2 test_one_check "W-002" "w02_role.*" test_one_check_expect "W-002" "w02.bad_if.if" 0 test_one_check_expect "W-002" "w02_system_nowarn.if" 0 test_one_check_expect "W-002" "w02_system_warn.if" 1 } @test "W-003" { test_one_check "W-003" "w03.if" test_one_check_expect "W-003" "w03_role.if" 4 test_one_check_expect "W-003" "w03_ta.if" 0 test_one_check_expect "W-003" "w03_alias.if" 0 test_one_check_expect "W-003" "w03_stub.if" 0 test_one_check_expect "W-003" "w03_system_nowarn.if" 0 test_one_check_expect "W-003" "w03_system_warn.if" 1 } @test "W-004" { test_one_check "W-004" "w04.fc" } @test "W-005" { test_one_check "W-005" "w05*" } @test "W-006" { test_one_check "W-006" "w06.if" } @test "W-007" { test_one_check "W-007" "w07.if" test_one_check_expect "W-007" "w07.0.te" 0 test_one_check "W-007" "w07.1.te" } @test "W-008" { test_one_check "W-008" "w08.1.te" test_one_check "W-008" "w08.2.te" } @test "W-009" { test_one_check "W-009" "w09.te" } @test "W-010" { test_one_check "W-010" "w10.warn.te" test_one_check_expect "W-010" "w10.pass.te" 0 } @test "W-011" { test_one_check "W-011" "w11.*" } @test "W-012" { test_one_check "W-012" "w12.te" } @test "W-013" { test_one_check_expect "W-013" "w13.te" 2 } @test "E-002" { test_one_check "E-002" "e02.fc" } @test "E-003" { test_one_check "E-003" "e03e04e05.fc" } @test "E-004" { test_one_check "E-004" "e03e04e05.fc" } @test "E-005" { test_one_check "E-005" "e03e04e05.fc" } @test "E-006" { test_one_check "E-006" "e06.*" } @test "E-007" { test_one_check_expect "E-007" "e07.warn.te" 5 test_one_check_expect "E-007" "e07.pass.te" 0 } @test "E-008" { test_one_check_expect "E-008" "e08.warn.te" 4 test_one_check_expect "E-008" "e08.pass.te" 0 } @test "E-009" { test_one_check_expect "E-009" "e09.te" 4 } @test "E-010" { test_one_check_expect "E-010" "e10.warn.te" 9 test_one_check_expect "E-010" "e10.pass.te" 0 } @test "assume_user" { touch tmp.conf do_test "E-003" "e03e04e05.fc" 1 "-e E-003" echo "assume_users = { system_u }" >> tmp.conf do_test "E-003" "e03e04e05.fc" 0 "-e E-003" rm tmp.conf } @test "assume_role" { touch tmp.conf do_test "E-004" "e03e04e05.fc" 1 "-e E-004" echo "assume_roles = { object_r }" >> tmp.conf do_test "E-004" "e03e04e05.fc" 0 "-e E-004" rm tmp.conf } @test "usage" { run ${SELINT_PATH} -c configs/empty.conf [ "$status" -eq 64 ] usage_presence=$(echo ${output} | grep -o "^Usage" | wc -l) [ "$usage_presence" -eq 1 ] run ${SELINT_PATH} -c configs/empty.conf -Z [ "$status" -eq 64 ] usage_presence=$(echo ${output} | grep -o "Usage" | wc -l) [ "$usage_presence" -eq 1 ] message_presence=$(echo ${output} | grep -o "invalid option -- 'Z'" | wc -l) [ "$message_presence" -eq 1 ] } @test "Enable/disable" { run ${SELINT_PATH} -c configs/empty.conf -e W-002 -e W-003 -d S-002 -d C-002 -r -s policies/check_triggers [ "$status" -eq 0 ] count=$(echo ${output} | grep -o "S-002" | wc -l) [ "$count" -eq 0 ] count=$(echo ${output} | grep -o "C-002" | wc -l) [ "$count" -eq 0 ] count=$(echo ${output} | grep -o "W-002" | wc -l) [ "$count" -gt 0 ] count=$(echo ${output} | grep -o "W-003" | wc -l) [ "$count" -gt 0 ] } @test "verbose mode" { run ${SELINT_PATH} -c configs/default.conf -r -s -v policies/check_triggers [ "$status" -eq 0 ] verbose_presence=$(echo ${output} | grep -o "^Verbose" | wc -l) [ "$verbose_presence" -eq 1 ] } @test "valgrind" { run command -v valgrind 2>&1 if [ "$status" -ne 0 ] && test -z "$CI"; then skip "valgrind not found" fi run valgrind --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 ${SELINT_PATH} -c configs/default.conf -r -s policies/check_triggers [ "$status" -eq 0 ] } @test "nesting_gen_req" { run ${SELINT_PATH} -c configs/default.conf -e W-002 -E -s policies/misc/nesting.* [ "$status" -eq 0 ] count=$(echo ${output} | grep -o "W-002" | wc -l) echo "Status: $status, Count: $count (expected 1)" echo $output [ "$count" -eq 1 ] count=$(echo ${output} | grep -o "foo_data_t" | wc -l) echo "Status: $status, Count: $count (expected 1)" echo $output [ "$count" -eq 1 ] count=$(echo ${output} | grep -o "foo_log_t" | wc -l) echo "Status: $status, Count: $count (expected 0)" echo $output [ "$count" -eq 0 ] } @test "disable comment" { run ${SELINT_PATH} -c configs/default.conf -F -e W-002 -E -s policies/misc/disable.* [ "$status" -eq 0 ] count=$(echo ${output} | grep -o "W-002" | wc -l) echo "Status: $status, Count: $count (expected 0)" echo $output [ "$count" -eq 0 ] echo "Part I" run ${SELINT_PATH} -F -s -c configs/default.conf policies/misc/disable_multiple* [ "$status" -eq 0 ] echo "Part II" run ${SELINT_PATH} -F -s -c configs/default.conf -d S-008 policies/misc/disable_require_start.* [ "$status" -eq 0 ] echo "Part III" run ${SELINT_PATH} -F -s -c configs/default.conf policies/misc/disable_require_decl.* [ "$status" -eq 0 ] } @test "nonexistent file" { run ${SELINT_PATH} -s -c configs/default.conf doesnt_exist.te [ "$status" -eq 70 ] run ${SELINT_PATH} -s -c configs/default.conf doesnt_exist.if [ "$status" -eq 70 ] run ${SELINT_PATH} -s -c configs/default.conf doesnt_exist.fc [ "$status" -eq 70 ] } @test "Broken config" { run command -v valgrind 2>&1 if [ "$status" -ne 0 ] && test -z "$CI"; then skip "valgrind not found" fi run valgrind --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 ${SELINT_PATH} -c configs/broken.conf -rs policies/check_triggers [ "$status" -eq 78 ] } @test "Bad check ids" { run ${SELINT_PATH} -s -c configs/default.conf policies/misc/no_issues.te count=$(echo ${output} | grep -o "Warning: Failed to locate modules.conf file." | wc -l) [ "$count" -eq 1 ] #"Failed to find a valid modules.conf" run ${SELINT_PATH} -s -c configs/default.conf -e foo policies/misc/no_issues.te count=$(echo ${output} | grep -o "not a valid check id" | wc -l) [ "$count" -eq 1 ] run ${SELINT_PATH} -s -c configs/default.conf -d foo policies/misc/no_issues.te count=$(echo ${output} | grep -o "not a valid check id" | wc -l) [ "$count" -eq 1 ] run ${SELINT_PATH} -s -c configs/bad_ids.conf policies/misc/no_issues.te count=$(echo ${output} | grep -o "not a valid check id" | wc -l) [ "$count" -eq 2 ] run ${SELINT_PATH} -s -c configs/bad_ids.conf -e foo -d bar -d baz policies/misc/no_issues.te count=$(echo ${output} | grep -o "not a valid check id" | wc -l) [ "$count" -eq 5 ] } @test "context flag" { touch tmp.conf do_test "W-001" "../misc/needs_context.te" 0 do_test "W-001" "../misc/needs_context.te" 1 "--context=policies/context" do_test "W-001" "../misc/needs_context.te" 1 "--context=policies/context2" do_test "W-001" "../misc/needs_context.te" 2 "--context=policies/context --context=policies/context2" rm tmp.conf } @test "run_summary" { run ${SELINT_PATH} -c configs/default.conf -rsS policies/check_triggers count=$(echo ${output} | grep -o "Found the following issue counts" | wc -l) [ "$count" -eq 1 ] for SEV in "C" "S" "W" "E" do count=$(echo ${output} | grep -E -o "${SEV}-00[0-9]: [0-9]+" | wc -l) [ "$count" -ge 1 ] done } @test "custom_fc_macros" { run ${SELINT_PATH} -c configs/default.conf -s policies/misc/fc_macros.fc count=$(echo ${output} | grep -o "E-002" | wc -l) echo ${output} [ "$count" -eq 1 ] run ${SELINT_PATH} -c configs/fc_macros.conf -s policies/misc/fc_macros.fc count=$(echo ${output} | grep -o "E-002" | wc -l) echo ${output} [ "$count" -eq 0 ] } @test "parse_error_printing" { test_parse_error_run 0 } @test "parse_error_printing_valgrind" { if [ -z ${BATS_TIME_EXPENSIVE} ]; then skip "BATS_TIME_EXPENSIVE not set" fi test_parse_error_run 1 } @test "report format" { test_report_format_impl '' test1.te test1.output test_report_format_impl --full-path test1.te test1.output.full test_report_format_impl --summary test1.te test1.output.summary test_report_format_impl '--level W' test1.te test1.output.warn test_report_format_impl --summary-only test1.te test1.output.summaryonly } selint-1.5.1/tests/functional/policies/000077500000000000000000000000001475050262500201355ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/000077500000000000000000000000001475050262500231205ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/000077500000000000000000000000001475050262500236005ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/decl_in_block.expect.lax000066400000000000000000000000001475050262500303320ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/decl_in_block.expect.ref000066400000000000000000000000001475050262500303220ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/decl_in_block.te000066400000000000000000000005071475050262500267030ustar00rootroot00000000000000policy_module(decl_in_block, 1.0) type foo_t; domain_type(foo_t) type foo_exec_t; allow foo_t self:capability mac_admin; ifdef(`init_systemd',` init_system_domain(foo_t, foo_exec_t) ') tunable_policy(`some_tunable',` # C-001 doesn't care if it's redundant domain_type(foo_t) ') optional_policy(` foo_writer(foo_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/if_in_optional.expect.lax000066400000000000000000000000001475050262500305540ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/if_in_optional.expect.ref000066400000000000000000000000001475050262500305440ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/if_in_optional.te000066400000000000000000000003741475050262500271270ustar00rootroot00000000000000policy_module(ifdef_in_optional, 1.0) type foo_t; domain_type(foo_t) allow foo_t self:tcp_socket { create_socket_perms rw_socket_perms }; optional_policy(` foo_do_stuff(foo_t) ') optional_policy(` ifdef(`bar',` foo_do_other_stuff(foo_t) ') ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/000077500000000000000000000000001475050262500257235ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/kernel/000077500000000000000000000000001475050262500272035ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/kernel/domain.if000066400000000000000000000011471475050262500307750ustar00rootroot00000000000000# This file contains stubbed interfaces for domains for use in test #Stubbed domain_type interface(`domain_type',` domain_other_interface_type($1) ') # Other interface is called to check transform interface calculations interface(`domain_other_interface_type',` gen_require(` attribute domain; ') typeattribute $1 domain; ') # Comment interface(`domain_first_access',` gen_require(` type domain_foo_t; ') read_files_pattern($1, domain_foo_t, domain_foo_t) ') # Comment interface(`domain_second_access',` gen_require(` type domain_bar_t; ') read_files_pattern($1, domain_bar_t, domain_bar_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/kernel/kernel.if000066400000000000000000000002651475050262500310060ustar00rootroot00000000000000# Fake interfaces for testing ordering # Comment interface(`kernel_read_system_state',` gen_require(` type system_state_t; ') allow $1 system_state_t:file read_file_perms; ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/other/000077500000000000000000000000001475050262500270445ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/other/bar.if000066400000000000000000000002261475050262500301300ustar00rootroot00000000000000# Fake interfaces for test # Do the things interface(`bar_do_stuff',` gen_require(` type stuff_t; ') do_things_pattern($1, stuff_t, stuff_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/other/mta.if000066400000000000000000000010001475050262500301340ustar00rootroot00000000000000# Fake interfaces for test # Do the things interface(`foo_do_stuff',` gen_require(` type stuff_t; ') do_things_pattern($1, stuff_t, stuff_t) ') # Need a second interface interface(`foo_do_other_stuff',` gen_require(` type other_stuff_t; ') do_things_pattern($1, other_stuff_t, other_stuff_t) ') # Transform interface interface(`foo_writer_domain',` gen_require(` attribute foo_writer; type foo_target_t; ') typeattribute $1 foo_writer; write_files_pattern($1, foo_target_t, foo_target_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/other/role_ifs.if000066400000000000000000000001501475050262500311620ustar00rootroot00000000000000interface(`role_ifs_associate_role',` gen_require(` role some_role; ') role some_role types $1; ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/other/xyz.if000066400000000000000000000002261475050262500302160ustar00rootroot00000000000000# Fake interfaces for test # Do the things interface(`xyz_do_stuff',` gen_require(` type stuff_t; ') do_things_pattern($1, stuff_t, stuff_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/system/000077500000000000000000000000001475050262500272475ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/system/init.if000066400000000000000000000003441475050262500305330ustar00rootroot00000000000000# Fake init interfaces # Fake init_system_domain interface(`init_system_domain',` gen_require(` type init_script_t; attribute someattribute; ') typeattribute $1 someattribute; domtrans_pattern(init_script_t, $1, $2) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interfaces/system/logging.if000066400000000000000000000006501475050262500312160ustar00rootroot00000000000000# Fake interfaces for testing C-001 # Comment interface(`logging_send_audit_msgs',` gen_require(` type audit_msgs_t; ') fake_send_pattern($1, audit_msgs_t) ') # Comment interface(`logging_read_audit_log',` gen_require(` type audit_log_t; ') read_files_pattern($1, audit_log_t, audit_log_t) ') #Comment interface(`logging_search_logs',` gen_require(` type log_t; ') search_dirs_pattern($1, log_t, log_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/interleaved.expect.lax000066400000000000000000000000031475050262500300700ustar00rootroot0000000000000015 selint-1.5.1/tests/functional/policies/check_triggers/C-001/interleaved.expect.ref000066400000000000000000000000061475050262500300630ustar00rootroot0000000000000014 15 selint-1.5.1/tests/functional/policies/check_triggers/C-001/interleaved.te000066400000000000000000000006201475050262500264320ustar00rootroot00000000000000policy_module(interleaved, 1.0) type domA_t; domain_type(domA_t) type domB_t; domain_type(domB_t) type resource_t; files_type(resource_t) manage_files_pattern(domA_t, resource_t, resource_t) logging_send_audit_msgs(domA_t) logging_send_audit_msgs(domB_t) kernel_read_system_state(domA_t) optional_policy(` apache_manage_config(domA_t) ') manage_files_pattern(domB_t, resource_t, resource_t) selint-1.5.1/tests/functional/policies/check_triggers/C-001/interleaved2.expect.lax000066400000000000000000000000061475050262500301550ustar00rootroot0000000000000019 20 selint-1.5.1/tests/functional/policies/check_triggers/C-001/interleaved2.expect.ref000066400000000000000000000000061475050262500301450ustar00rootroot0000000000000019 20 selint-1.5.1/tests/functional/policies/check_triggers/C-001/interleaved2.te000066400000000000000000000006471475050262500265250ustar00rootroot00000000000000policy_module(interleaved2, 1.0) type dom_t; domain_type(dom_t) type dom_helper_t; domain_type(dom_helper_t) type resource_t; files_type(resource_t) allow dom_t self:capability setuid; allow dom_t self:process getpid; manage_files_pattern(dom_t, resource_t, resource_t) allow dom_helper_t self:capability setuid; allow dom_helper_t self:process getpid; optional_policy(` logging_send_audit_msgs(dom_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/kernel_module_first.expect.lax000066400000000000000000000000001475050262500316170ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/kernel_module_first.expect.ref000066400000000000000000000000031475050262500316120ustar00rootroot0000000000000011 selint-1.5.1/tests/functional/policies/check_triggers/C-001/kernel_module_first.te000066400000000000000000000003621475050262500301670ustar00rootroot00000000000000policy_module(kernel_module_first, 1.0) type foo_t; domain_type(foo_t) allow foo_t self:capability { sys_admin net_admin lease }; domain_first_access(foo_t) domain_second_access(foo_t) kernel_read_system_state(foo_t) foo_do_stuff(foo_t) selint-1.5.1/tests/functional/policies/check_triggers/C-001/optional.expect.lax000066400000000000000000000000001475050262500274100ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/optional.expect.ref000066400000000000000000000000031475050262500274030ustar00rootroot0000000000000011 selint-1.5.1/tests/functional/policies/check_triggers/C-001/optional.te000066400000000000000000000004361475050262500257620ustar00rootroot00000000000000policy_module(optional, 1.0) type foo_t; domain_type(foo_t) allow foo_t self:capability dac_override; kernel_read_system_state(foo_t) optional_policy(` foo_do_stuff(foo_t) ') tunable_policy(`foo_read_audit',` logging_read_audit_log(foo_t) logging_set_audit_parameters(foo_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/optional_optional.expect.lax000066400000000000000000000000001475050262500313150ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/optional_optional.expect.ref000066400000000000000000000000001475050262500313050ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/optional_optional.te000066400000000000000000000003531475050262500276650ustar00rootroot00000000000000policy_module(optional_optional, 1.0) type dom_t; domain_type(dom_t) kernel_read_system_state(dom_t) optional_policy(` foo_do_stuff(dom_t) optional_policy(` xyz_do_stuff(dom_t) ') ') optional_policy(` bar_do_stuff(dom_t) ') selint-1.5.1/tests/functional/policies/check_triggers/C-001/role_ifs.expect.lax000066400000000000000000000000031475050262500273700ustar00rootroot0000000000000011 selint-1.5.1/tests/functional/policies/check_triggers/C-001/role_ifs.expect.ref000066400000000000000000000000031475050262500273600ustar00rootroot0000000000000011 selint-1.5.1/tests/functional/policies/check_triggers/C-001/role_ifs.te000066400000000000000000000003031475050262500257300ustar00rootroot00000000000000policy_module(role_ifs, 1.0) type foo_t; role_ifs_associate_role(foo_t) type bar_t; allow foo_t self:capability mac_override; allow foo_t bar_t:process signal; role_ifs_associate_role(bar_t) selint-1.5.1/tests/functional/policies/check_triggers/C-001/self_macro.expect.lax000066400000000000000000000000001475050262500276750ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/self_macro.expect.ref000066400000000000000000000000001475050262500276650ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/self_macro.te000066400000000000000000000004051475050262500262430ustar00rootroot00000000000000policy_module(self_macro, 1.0) type foo_t; domain_type(foo_t) allow foo_t self:process signal; allow foo_t self:tcp_socket read; tcp_socket_pattern(foo_t, self) allow foo_t self:udp_socket read; udp_socket_pattern(foo_t, self) allow foo_t self:passwd passwd; selint-1.5.1/tests/functional/policies/check_triggers/C-001/simple.expect.lax000066400000000000000000000000031475050262500270570ustar00rootroot0000000000000013 selint-1.5.1/tests/functional/policies/check_triggers/C-001/simple.expect.ref000066400000000000000000000000031475050262500270470ustar00rootroot0000000000000013 selint-1.5.1/tests/functional/policies/check_triggers/C-001/simple.te000066400000000000000000000003001475050262500254140ustar00rootroot00000000000000policy_module(simple, 1.0) type foo_t; type foo_data_t; manage_files_pattern(foo_t, foo_data_t, foo_data_t) kernel_read_system_state(foo_t) logging_search_logs(foo_t) domain_type(foo_t) selint-1.5.1/tests/functional/policies/check_triggers/C-001/types_in_requires.expect.lax000066400000000000000000000000001475050262500313340ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/types_in_requires.expect.ref000066400000000000000000000000001475050262500313240ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/check_triggers/C-001/types_in_requires.te000066400000000000000000000002461475050262500277050ustar00rootroot00000000000000policy_module(types_in_requires, 1.0) type foo_t; allow foo_t self:process setuid; require { type bar_log_t; } append_files_pattern(foo_t, bar_log_t, bar_log_t) selint-1.5.1/tests/functional/policies/check_triggers/access_vectors000066400000000000000000000001621475050262500260500ustar00rootroot00000000000000class file { read open } class dir { read open search } class system { ipc_info halt } class dbus { send_msg } selint-1.5.1/tests/functional/policies/check_triggers/c04.if000066400000000000000000000001361475050262500240260ustar00rootroot00000000000000interface(`foo_read',` gen_require(` type foo_t; ') read_file_perms($1, foo_t, foo_t) ') selint-1.5.1/tests/functional/policies/check_triggers/c05.if000066400000000000000000000001461475050262500240300ustar00rootroot00000000000000interface(`bar',` gen_require(` class foo { perm2 perm1 }; ') allow source target:foo perm1; ') selint-1.5.1/tests/functional/policies/check_triggers/c05.te000066400000000000000000000001441475050262500240400ustar00rootroot00000000000000policy_module(c05, 1.0) allow source target:cls { foo bar }; allow source target:cls { foo foo }; selint-1.5.1/tests/functional/policies/check_triggers/c06.pass.if000066400000000000000000000004071475050262500247760ustar00rootroot00000000000000# comment interface(`c06_interface',` gen_require(` bool c06_bool; class a { a_perm }; class b { b_perm }; role sysadm_r; attribute_role c06_roles; attribute c06_domain; type c06_t, c06_exec_t; type c06_tmp_t; type c06_unit_t; ') # empty ') selint-1.5.1/tests/functional/policies/check_triggers/c06.warn.if000066400000000000000000000004171475050262500250000ustar00rootroot00000000000000# comment interface(`c06_fail_interface',` gen_require(` type foo; class bar { bar_perm }; ') gen_require(` type foo, bar; ') gen_require(` type foo; type bar; ') gen_require(` type bar_conf_t, bar_t; ') gen_require(` type foo_t, foo_t; ') ') selint-1.5.1/tests/functional/policies/check_triggers/c07.if000066400000000000000000000002271475050262500240320ustar00rootroot00000000000000template(`foo',` allow $1_t $1_t:process signal; allow $2_t self:process signal; allow $1 $1:fd use; # No issue because $1 might be an attribute ') selint-1.5.1/tests/functional/policies/check_triggers/c07.te000066400000000000000000000001541475050262500240430ustar00rootroot00000000000000policy_module(c07, 1.0) type foo_t; attribute foo; allow foo_t foo_t:file read; allow foo foo:file read; selint-1.5.1/tests/functional/policies/check_triggers/c08.te000066400000000000000000000000651475050262500240450ustar00rootroot00000000000000policy_module(c08, 1.0) type foo_t; bool c08_cond; selint-1.5.1/tests/functional/policies/check_triggers/c08_other.te000066400000000000000000000002551475050262500252470ustar00rootroot00000000000000policy_module(c08_other, 1.0) type bar_t; bool c08_other_cond; if (c08_other_cond) { allow bar_t self:process fork; } if (c08_cond) { allow bar_t self:process fork; } selint-1.5.1/tests/functional/policies/check_triggers/e02.fc000066400000000000000000000001001475050262500240070ustar00rootroot00000000000000/usr/bin/foo -- gen_deadbeef(system_u:object_r:foo_exec_t, s0) selint-1.5.1/tests/functional/policies/check_triggers/e03e04e05.fc000066400000000000000000000001011475050262500246340ustar00rootroot00000000000000/usr/bin/foo -- gen_context(system_u:object_r:foo_exec_t, s0) selint-1.5.1/tests/functional/policies/check_triggers/e06.if000066400000000000000000000000311475050262500240240ustar00rootroot00000000000000interface(`clashes',` ') selint-1.5.1/tests/functional/policies/check_triggers/e06.te000066400000000000000000000000541475050262500240430ustar00rootroot00000000000000policy_module(e06, 1.0) attribute clashes; selint-1.5.1/tests/functional/policies/check_triggers/e07.pass.te000066400000000000000000000003431475050262500250120ustar00rootroot00000000000000policy_module(e07_pass, 1.0) allow source target:file read; allow source target:file { open read }; allow source target:file read_file_perms; # TODO: class 'file' has no permission 'search' allow source target:file search; selint-1.5.1/tests/functional/policies/check_triggers/e07.warn.te000066400000000000000000000003351475050262500250140ustar00rootroot00000000000000policy_module(e07_warn, 1.0) allow source target:file READ; allow source target:file some; allow source target:file { read some }; allow source target:file reed_file_perms; allow source target:file read_file_permss; selint-1.5.1/tests/functional/policies/check_triggers/e08.pass.te000066400000000000000000000003401475050262500250100ustar00rootroot00000000000000policy_module(e08_pass, 1.0) allow source target:file read; allow source target:{ dir file } { open read }; type_transition source entry:dir target; role_transition source entry:dir target; allow source target:$2 read; selint-1.5.1/tests/functional/policies/check_triggers/e08.warn.te000066400000000000000000000002721475050262500250150ustar00rootroot00000000000000policy_module(e08_warn, 1.0) allow source target:File read; allow source target:{ dir File } read; type_transition source entry:Dir target; role_transition source entry:dirr target; selint-1.5.1/tests/functional/policies/check_triggers/e09.te000066400000000000000000000002611475050262500240460ustar00rootroot00000000000000policy_module(e09, 1.0) type foo_t; optional_policy(`') optional_policy(` #can_exec(foo_t, bar_t) ') optional_policy(` gen_require(` #comment ') ') require { #comment } selint-1.5.1/tests/functional/policies/check_triggers/e10.pass.te000066400000000000000000000006321475050262500250050ustar00rootroot00000000000000policy_module(e10_pass, 1.0) ignored_bare_word define(`not_really_a_bare_word',` ignored_bare_word ') define(not_really_a_bare_word, ` ignored_bare_word ') ifdef(`foo',` # comment ignored_bare_word ') ifelse(`bar', `true', ` ignored_bare_word ',`$2', true, ` ignored_bare_word ') ifelse(`bar', `true', ` ignored_bare_word ',`$2', true, ` ignored_bare_word', ` ignored_bare_word ') selint-1.5.1/tests/functional/policies/check_triggers/e10.warn.te000066400000000000000000000005711475050262500250100ustar00rootroot00000000000000policy_module(e10_warn, 1.0) bare_word bare word ifdef(`foo',` # comment bare_word ') ifelse(`bar', `true', ` bare_word ',`$2', `true', ` bare_word ') ifelse(`bar', `true', ` bare_word ',`$2', `true', ` bare_word', ` bare_word ') # NOTE: a bare word inside define() body currently doesn't get flagged define(`not_really_a_bare_word',` bare_word ') selint-1.5.1/tests/functional/policies/check_triggers/modules.conf000066400000000000000000000000401475050262500254310ustar00rootroot00000000000000w05 = module w05_other = module selint-1.5.1/tests/functional/policies/check_triggers/obj_perm_sets.spt000066400000000000000000000000751475050262500265050ustar00rootroot00000000000000define(`read_file_perms',`{ read open getattr lock ioctl }') selint-1.5.1/tests/functional/policies/check_triggers/s01.te000066400000000000000000000000621475050262500240530ustar00rootroot00000000000000policy_module(s01, 1.0) require { type foo_t; } selint-1.5.1/tests/functional/policies/check_triggers/s02.fc000066400000000000000000000001011475050262500240260ustar00rootroot00000000000000/usr/bin/foo -- gen_context(system_u:object_r:foo_exec_t, s0) selint-1.5.1/tests/functional/policies/check_triggers/s02_other.te000066400000000000000000000000601475050262500252530ustar00rootroot00000000000000policy_module(s02_other, 1.0) type foo_exec_t; selint-1.5.1/tests/functional/policies/check_triggers/s03.te000066400000000000000000000000431475050262500240540ustar00rootroot00000000000000policy_module(s03, 1.0) foo(bar); selint-1.5.1/tests/functional/policies/check_triggers/s04.if000066400000000000000000000000611475050262500240430ustar00rootroot00000000000000template(`foo',` ') interface(`bar',` foo() ') selint-1.5.1/tests/functional/policies/check_triggers/s05.if000066400000000000000000000000411475050262500240420ustar00rootroot00000000000000interface(`bar',` type $1_t; ') selint-1.5.1/tests/functional/policies/check_triggers/s06.te000066400000000000000000000000311475050262500240540ustar00rootroot00000000000000module bar 1.2; type a; selint-1.5.1/tests/functional/policies/check_triggers/s07.fc000066400000000000000000000000721475050262500240420ustar00rootroot00000000000000/usr/bin/foo -- gen_context(system_u:object_r:foo_exec_t) selint-1.5.1/tests/functional/policies/check_triggers/s08.if000066400000000000000000000001041475050262500240450ustar00rootroot00000000000000interface(`bar',` gen_require( type foo; ) some_macro(foo) ') selint-1.5.1/tests/functional/policies/check_triggers/s09.pass.te000066400000000000000000000010751475050262500250350ustar00rootroot00000000000000policy_module(s09, 1.0) allow source target:foo some_foo_perms; allow source target:file write_file_perms; allow source target:fifo_file write_fifo_file_perms; allow source target:netlink_something_socket some_netlink_socket_perms; allow source target:netlink_something_socket some_socket_perms; allow source target:something_socket some_socket_perms; allow source target:chr_file rw_term_perms; allow source target:process signal_perms; allow source target:{ tcp_socket udp_socket } create_socket_perms; allow source target:socket_class_set create_socket_perms; selint-1.5.1/tests/functional/policies/check_triggers/s09.warn.te000066400000000000000000000005201475050262500250300ustar00rootroot00000000000000policy_module(s09, 1.0) allow source target:foo some_bar_perms; allow source target:file write_lnk_file_perms; allow source target:something_socket some_netlink_socket_perms; allow source target:blk_file rw_term_perms; allow source target:{ file fifo_file } read_file_perms; allow source target:{ fifo_file file } read_file_perms; selint-1.5.1/tests/functional/policies/check_triggers/security_classes000066400000000000000000000001031475050262500264210ustar00rootroot00000000000000class file class dir class ipc class system class dbus # userspace selint-1.5.1/tests/functional/policies/check_triggers/w01.te000066400000000000000000000004141475050262500240600ustar00rootroot00000000000000policy_module(w01, 1.0) type bar_t; role bar_r; allow foo_t bar_t:file read; do_something(foo_domain) role foo_roles types bar_t; typeattribute bar_t foo_domain; roleattribute bar_r foo_roles; allow bar_t bar_t:dbus send_msg; allow bar_t bar_t:system ipc_info; selint-1.5.1/tests/functional/policies/check_triggers/w01_other.te000066400000000000000000000001351475050262500252610ustar00rootroot00000000000000policy_module(w01_other, 1.0) type foo_t; attribute foo_domain; attribute_role foo_roles; selint-1.5.1/tests/functional/policies/check_triggers/w02.bad_if.if000066400000000000000000000011751475050262500252570ustar00rootroot00000000000000## dummy.if ####################################### ## ## A template ## ## ## ## The prefix of the dummy (e.g., foo ## is the prefix for foo_t). ## ## # template(`dummy_template',` gen_require(` type etc_t; ') type $1_t; domain_type($1_t) allow $1_t etc_t:file read_file_perms; ') template(`basic_template',` gen_require(` type basic_t; ') type $1_basic_t; allow $1_basic_t basic_t:process signal; ') interface(`bad_if',` dummy_template($1) ') selint-1.5.1/tests/functional/policies/check_triggers/w02.if000066400000000000000000000003151475050262500240470ustar00rootroot00000000000000interface(`foo_do_things',` gen_require(` type foo_conf_t; ') allow $1 foo_conf_t:file read; read_files_pattern($1, bin_t, bin_t) ') template(`foo_attr_in_type_decl',` type $1_foo_t, foo_attr; ') selint-1.5.1/tests/functional/policies/check_triggers/w02.te000066400000000000000000000000721475050262500240610ustar00rootroot00000000000000policy_module(w02, 1.0) attribute foo_attr; type bin_t; selint-1.5.1/tests/functional/policies/check_triggers/w02_role.if000066400000000000000000000002221475050262500250650ustar00rootroot00000000000000interface(`foo_do_things',` gen_require(` type foo_conf_t; ') allow $1 foo_conf_t:file read; do_role_access($1, foo_conf_t, foo_conf_r) ') selint-1.5.1/tests/functional/policies/check_triggers/w02_role.te000066400000000000000000000000521475050262500251000ustar00rootroot00000000000000policy_module(w02, 1.0) role foo_conf_r; selint-1.5.1/tests/functional/policies/check_triggers/w02_system_nowarn.if000066400000000000000000000001051475050262500270340ustar00rootroot00000000000000#comment interface(`foo_ipc_info',` allow $1 $1:system ipc_info; ') selint-1.5.1/tests/functional/policies/check_triggers/w02_system_warn.if000066400000000000000000000001051475050262500264770ustar00rootroot00000000000000#comment interface(`foo_systemd_halt',` allow $1 $1:system halt; ') selint-1.5.1/tests/functional/policies/check_triggers/w03.if000066400000000000000000000011771475050262500240570ustar00rootroot00000000000000######################################## ## ## Execute racoon and allow the specified role the domain. ## ## ## ## Domain allowed to transition. ## ## ## ## ## Role allowed access. ## ## ## # # interface(`ipsec_run_racoon',` gen_require(` type racoon_t; ') ipsec_domtrans_racoon($1) role $2 types racoon_t; ') # Comment interface(`this_one_fails',` gen_require(` type foo_t, bar_t; ') allow $1 foo_t:file read; ') selint-1.5.1/tests/functional/policies/check_triggers/w03_alias.if000066400000000000000000000001321475050262500252160ustar00rootroot00000000000000template(`foo_alias_domain',` gen_require(` type foo_t; ') type $1_t alias foo_t; ') selint-1.5.1/tests/functional/policies/check_triggers/w03_role.if000066400000000000000000000020111475050262500250640ustar00rootroot00000000000000######################################## ## ## Execute racoon and allow the specified role the domain. ## ## ## ## Domain allowed to transition. ## ## ## ## ## Role allowed access. ## ## ## # # interface(`ipsec_run_racoon',` gen_require(` type racoon_t; ') ipsec_domtrans_racoon($1) role $2 types racoon_t; ') # Comment interface(`this_one_fails',` gen_require(` type foo_t; role foo_r; ') allow $1 foo_t:file read; ') # Comment interface(`this_one_fails2',` gen_require(` type foo_t; class file { read }; ') allow $1 foo_t:file read; ') # Comment interface(`this_one_fails3',` gen_require(` type foo_t; class dir { read }; ') allow $1 foo_t:file read; ') # Comment interface(`this_one_fails4',` gen_require(` type foo_t; class dbus { send_msg }; ') allow $1 foo_t:file read; ') selint-1.5.1/tests/functional/policies/check_triggers/w03_stub.if000066400000000000000000000001041475050262500251010ustar00rootroot00000000000000#comment interface(`w03_stub',` gen_require(` type w03_t; ') ') selint-1.5.1/tests/functional/policies/check_triggers/w03_system_nowarn.if000066400000000000000000000001611475050262500270370ustar00rootroot00000000000000#comment interface(`foo_halt_systemd',` gen_require(` class system { halt }; ') allow $1 $1:system halt; ') selint-1.5.1/tests/functional/policies/check_triggers/w03_system_warn.if000066400000000000000000000001651475050262500265060ustar00rootroot00000000000000#comment interface(`foo_ipc_info',` gen_require(` class system { ipc_info }; ') allow $1 $1:system ipc_info; ') selint-1.5.1/tests/functional/policies/check_triggers/w03_ta.if000066400000000000000000000001551475050262500245360ustar00rootroot00000000000000#comment interface(`foo_do_things',` gen_require(` attribute foo_doer; ') typeattribute $1 foo_doer; ') selint-1.5.1/tests/functional/policies/check_triggers/w04.fc000066400000000000000000000001161475050262500240420ustar00rootroot00000000000000/usr/bin/file_with_dot.txt -- gen_context(system_u:object_r:user_home_t, s0) selint-1.5.1/tests/functional/policies/check_triggers/w05.te000066400000000000000000000000661475050262500240670ustar00rootroot00000000000000policy_module(w05, 1.0) type bar_t; foo_read(bar_t) selint-1.5.1/tests/functional/policies/check_triggers/w05_other.if000066400000000000000000000000711475050262500252520ustar00rootroot00000000000000interface(`foo_read',` read_files_patten($1, $1, $1) ') selint-1.5.1/tests/functional/policies/check_triggers/w06.if000066400000000000000000000000731475050262500240540ustar00rootroot00000000000000interface(`bar',` # empty ') interface(`foo',` bar() ') selint-1.5.1/tests/functional/policies/check_triggers/w07.0.te000066400000000000000000000000621475050262500242230ustar00rootroot00000000000000policy_module(w07, 1.0) foo(s0 - mls_systemhigh) selint-1.5.1/tests/functional/policies/check_triggers/w07.1.te000066400000000000000000000000761475050262500242310ustar00rootroot00000000000000policy_module(w07, 1.0) attribute a1; type t1; foo(a1 - t1) selint-1.5.1/tests/functional/policies/check_triggers/w07.if000066400000000000000000000001021475050262500240460ustar00rootroot00000000000000interface(`bar',` # empty ') interface(`foo',` bar(one two) ') selint-1.5.1/tests/functional/policies/check_triggers/w08.1.te000066400000000000000000000000641475050262500242270ustar00rootroot00000000000000policy_module(w08, 1.0) allow source target:cls *; selint-1.5.1/tests/functional/policies/check_triggers/w08.2.te000066400000000000000000000001031475050262500242220ustar00rootroot00000000000000policy_module(w08, 1.0) allow source target:cls ~{ perm1 perm2 }; selint-1.5.1/tests/functional/policies/check_triggers/w09.te000066400000000000000000000000541475050262500240700ustar00rootroot00000000000000policy_module(different_name, 1.0) type a; selint-1.5.1/tests/functional/policies/check_triggers/w10.pass.te000066400000000000000000000001631475050262500250260ustar00rootroot00000000000000policy_module(w10, 1.0.0) type t1; somethingsomething_pattern(t1) type t2; can_exec(t1, t2) w06_interface(t1) selint-1.5.1/tests/functional/policies/check_triggers/w10.warn.te000066400000000000000000000001341475050262500250250ustar00rootroot00000000000000policy_module(w10, 1.0.0) type t; # module w05 is defined in modules.conf w05_nonexist(t) selint-1.5.1/tests/functional/policies/check_triggers/w11.if000066400000000000000000000001121475050262500240420ustar00rootroot00000000000000interface(`w11_if',` gen_require(` type w11_t; type wnot11_t; ') ') selint-1.5.1/tests/functional/policies/check_triggers/w11.te000066400000000000000000000000471475050262500240630ustar00rootroot00000000000000policy_module(w11, 1.0.0) type w11_t; selint-1.5.1/tests/functional/policies/check_triggers/w12.te000066400000000000000000000002421475050262500240610ustar00rootroot00000000000000policy_module(w12, 1.0) type bar_t; bool w12_cond; if (w12_cond) { allow bar_t self:process fork; } if (w12_cond && foo) { allow bar_t self:process fork; } selint-1.5.1/tests/functional/policies/check_triggers/w13.te000066400000000000000000000004711475050262500240660ustar00rootroot00000000000000policy_module(w13, 1.0) type foo_t; # do not warn about these allow foo_t foo_t:file *; allow foo_t foo_t:dir ~map; dontaudit foo_t foo_t:chr_file audit_access; neverallow foo_t foo_t:sock_file audit_access; # warn about these allow foo_t foo_t:file audit_access; auditallow foo_t foo_t:lnk_file audit_access; selint-1.5.1/tests/functional/policies/check_triggers/x01.if000066400000000000000000000000651475050262500240510ustar00rootroot00000000000000interface(`used_if',` ') interface(`unused_if',` ') selint-1.5.1/tests/functional/policies/check_triggers/x01.te000066400000000000000000000000651475050262500240630ustar00rootroot00000000000000policy_module(x01, 0.1) type foo_t; used_if(foo_t) selint-1.5.1/tests/functional/policies/check_triggers/x02.te000066400000000000000000000005251475050262500240650ustar00rootroot00000000000000policy_module(x02, 0.1) attribute attr; type foo_t; allow { attr -foo_t } foo_t:process signal; allow foo_t { attr -foo_t }:process signal; auditallow foo_t { attr -foo_t }:process signal; dontaudit foo_t { attr -foo_t }:process signal; neverallow foo_t { attr -foo_t }:process signal; manage_files_pattern(foo_t, attr, { attr -foo_t }) selint-1.5.1/tests/functional/policies/context/000077500000000000000000000000001475050262500216215ustar00rootroot00000000000000selint-1.5.1/tests/functional/policies/context/context.if000066400000000000000000000003511475050262500236240ustar00rootroot00000000000000## Interfaces to test the --context selint flagInterfaces to test the --context selint flag Some interfaces for test # Comments get ignored interface(`basic_domtrans',` gen_require(` type basic_t, basic_exec_t; ') domtrans_pattern($1, basic_exec_t, basic_t) ') template(`basic_template',` gen_require(` type basic_t; ') type $1_basic_t; allow $1_basic_t basic_t:process signal; ') selint-1.5.1/tests/sample_policy_files/basic.te000066400000000000000000000006741475050262500216300ustar00rootroot00000000000000policy_module(basic) type basic_t; type basic_exec_t; allow basic_t basic_exec_t:file { read execute entrypoint }; macro1(basic_t) macro2(basic_t, basic_exec_t) macro3(basic_t basic_exec_t) macro4(basic_t, basic_t basic_exec_t, { basic_t -basic_exec_t }) optional_policy(` allow basic_t foo_t:file *; allow basic_t foo_t:dir read; ') optional_policy(` allow basic_t bar_t:file ~{ read write }; ') allow basic_t self:capability chown; selint-1.5.1/tests/sample_policy_files/blocks.te000066400000000000000000000001171475050262500220140ustar00rootroot00000000000000policy_module(blocks, 1.0) # Empty optional_policy block optional_policy(` ') selint-1.5.1/tests/sample_policy_files/bool_declarations.te000066400000000000000000000002571475050262500242270ustar00rootroot00000000000000policy_module(bool_declarations, 1.0.0) bool bool_one; gen_bool(bool_two, false) gen_bool(bool_three,true) gen_tunable(`tunable_one', true) gen_tunable(tunable_two,false) selint-1.5.1/tests/sample_policy_files/declaring_template.if000066400000000000000000000005021475050262500243460ustar00rootroot00000000000000template(`declaring_template',` type prefix_$1_suffix; type $2_t; role $2_r; ') interface(`declaring_helper_if',` # this will not declare the types in the current module # but there will be a S-004 issued declaring_template(hello, world) ') template(`declaring_helper_temp',` declaring_template(good, morning) ') selint-1.5.1/tests/sample_policy_files/declaring_template.te000066400000000000000000000003221475050262500243600ustar00rootroot00000000000000policy_module(declaring_template, 1.0.0) declaring_template(foo, bar) # this will not declare the types in the current module # but there will be a S-004 issued declaring_helper_if() declaring_helper_temp() selint-1.5.1/tests/sample_policy_files/disable_booltunable.te000066400000000000000000000004421475050262500245310ustar00rootroot00000000000000policy_module(booltunable, 1.0) type foo_t; tunable_policy(tunable1 #selint-disable:C-008 ,` allow foo_t foo_t:cls perm; ') tunable_policy(`tunable2 && tunable3' #selint-disable:C-008 ,` allow foo_t foo_t:cls perm; ') if (bool1) #selint-disable:C-008 { allow foo_t foo_t:cls perm; } selint-1.5.1/tests/sample_policy_files/disable_comment.if000066400000000000000000000000651475050262500236540ustar00rootroot00000000000000interface(`foo', ` #selint-disable:S-012 # empty ') selint-1.5.1/tests/sample_policy_files/disable_comment.te000066400000000000000000000001361475050262500236650ustar00rootroot00000000000000policy_module(comment, 1.0) type foo_t; allow foo_t bar_t:file write; #selint-disable:W-001 selint-1.5.1/tests/sample_policy_files/disable_require.if000066400000000000000000000004221475050262500236630ustar00rootroot00000000000000interface(`foo1', ` gen_require(` class bar1_c { perm }; #selint-disable: W-010 role bar1_r; #selint-disable: W-011 bool bar1_b; #selint-disable: W-012 ') # empty ') interface(`foo2', ` gen_require(` type bar3_t, bar4_t; #selint-disable: W-011 ') #empty ') selint-1.5.1/tests/sample_policy_files/empty.te000066400000000000000000000000001475050262500216640ustar00rootroot00000000000000selint-1.5.1/tests/sample_policy_files/extended_perms.te000066400000000000000000000012621475050262500235470ustar00rootroot00000000000000policy_module(extended_perms, 1.0) type basic_t; type basic_dev_t; allow basic_t basic_dev_t:chr_file ioctl; allowxperm basic_t basic_dev_t:chr_file ioctl ~0x8927; allowxperm basic_t basic_dev_t:chr_file ioctl 35072; allowxperm basic_t basic_dev_t:chr_file ioctl { 0027 0028 }; allowxperm basic_t basic_dev_t:chr_file ioctl { 0 0x00 }; allowxperm basic_t basic_dev_t:chr_file ioctl { 0x0000 - 0x00ff }; allowxperm basic_t basic_dev_t:chr_file ioctl { 1024 - 2048 }; dontauditxperm basic_t basic_dev_t:chr_file ioctl { 1024-2048 35072 }; auditallowxperm basic_t basic_dev_t:chr_file ioctl ioctl_macro; neverallowxperm basic_t basic_dev_t:chr_file ioctl { ioctl_macro 0x40ff-0x41ff }; selint-1.5.1/tests/sample_policy_files/ifdef.if000066400000000000000000000004641475050262500216070ustar00rootroot00000000000000ifdef(`some_def',` interface(`foo',` gen_require(` type foo_t; ') some_call() ') ifndef(`some_other_def',` interface(`bar',` gen_require(` type bar_t; ') call_when_true() ') ',` interface(`bar',` gen_require(` type bar_t; ') call_when_false() ') ') #some_other_def ') #some_def selint-1.5.1/tests/sample_policy_files/ifdef_block.te000066400000000000000000000006161475050262500227720ustar00rootroot00000000000000policy_module(ifdef_block, 1.0) ifelse(`bool1', `true', ` # comment allow source1 target1:cls perm1; # comment allow source1 target1:cls perm2; ', `bool2', `true', ` # comment allow source2 target2:cls perm1; # comment allow source2 target2:cls perm2; ', ` # comment allow source3 target3:cls perm1; # comment allow source3 target3:cls perm2; ') selint-1.5.1/tests/sample_policy_files/modules.conf000066400000000000000000000002251475050262500225240ustar00rootroot00000000000000# This is an example modules.conf # It has comments that can include = characters # And blank lines sysadm = base # sudo = module games = off selint-1.5.1/tests/sample_policy_files/nested_templates.if000066400000000000000000000003111475050262500240610ustar00rootroot00000000000000## Nesting of template calls template(`outer',` middle($1,$3,$2) ') template(`middle',` inner($1, $2, $3) ') template(`inner',` type $1_t; type $2_foo_t; type $3_bar_t; ') selint-1.5.1/tests/sample_policy_files/none_context.fc000066400000000000000000000000571475050262500232250ustar00rootroot00000000000000/path/path <> /path/path2 <> selint-1.5.1/tests/sample_policy_files/obj_perm_sets.spt000066400000000000000000000012241475050262500235700ustar00rootroot00000000000000######################################## # # Macros for sets of classes # # # All directory and file classes # define(`dir_file_class_set', `{ dir file_class_set }') ######################################## # # Macros for sets of permissions # # # Permissions to mount and unmount file systems. # define(`mount_fs_perms', `{ mount remount unmount getattr }') # deprecated define(`remount_fs_perms',` # deprecated { remount getattr } refpolicywarn(`remount_fs_perms is deprecated') ') # # Permissions for using sockets. # define(`rw_socket_perms', `{ ioctl read getattr write setattr append bind connect getopt setopt shutdown }') selint-1.5.1/tests/sample_policy_files/perms.spt000066400000000000000000000024131475050262500220640ustar00rootroot00000000000000define(`getattr_dir_perms', `{ getattr }') define(`search_dir_perms', `{ open search getattr_dir_perms }') define(`read_dir_perms', `{ ioctl lock read search_dir_perms }') define(`add_name_dir_perms', `{ add_name ioctl lock write search_dir_perms }') define(`remove_name_dir_perms', `{ ioctl lock remove_name write search_dir_perms }') define(`rw_dir_perms', `{ add_name_dir_perms remove_name_dir_perms read_dir_perms }') define(`control_dir_perms', `{ create setattr link unlink rename reparent rmdir rw_dir_perms }') define(`relabel_dir_perms', `{ relabelfrom relabelto getattr_dir_perms }') define(`uncovered_dir_perms', `{ search open audit_access }') define(`getattr_file_perms', `{ getattr }') define(`read_no_lock_file_perms', `{ ioctl open read getattr_file_perms }') define(`read_file_perms', `{ lock read_no_lock_file_perms }') define(`write_file_perms', `{ ioctl lock open write getattr_file_perms }') define(`rw_no_open_file_perms', `{ ioctl lock read write getattr_file_perms }') define(`rw_file_perms', `{ open rw_no_open_file_perms }') define(`rename_file_perms', `{ rename getattr_file_perms }') define(`control_file_perms', `{ append create link rename setattr unlink rw_file_perms }') define(`relabel_file_perms', `{ relabelfrom relabelto getattr_file_perms }') selint-1.5.1/tests/sample_policy_files/syntax_error.te000066400000000000000000000001561475050262500233010ustar00rootroot00000000000000policy_module(syntax_error, 1.0 type no_semicolon_t allow no_semicolon_t other_thing_t:file { read write }; selint-1.5.1/tests/sample_policy_files/uncommon.te000066400000000000000000000077461475050262500224110ustar00rootroot00000000000000module uncommon 1; # Use uncommon policy constructs to ensure the parser can handle them without breaking require { type bar_t; role system_r, user_r; } type baz_t alias { old_baz_t older_baz_t }; type this_shouldnt_be_allowed_t alias other_name_t, attr_name; permissive foo_t; sid netmsg gen_context(system_u:object_r:netlabel_peer_t,mls_systemhigh) portcon udp 7007 gen_context(system_u:object_r:afs_bos_port_t,s0) portcon udp 7007 gen_context(system_u:object_r:afs_bos_port_t,s0:c2) portcon udp 7007 gen_context(system_u:object_r:afs_bos_port_t,s0,s1:c0.c225) portcon udp 7007-7008 gen_context(system_u:object_r:afs_bos_port_t,s0) fs_use_trans devtmpfs gen_context(system_u:object_r:device_t,s0); genfscon sysfs /devices/system/cpu/online gen_context(system_u:object_r:cpu_online_t,s0) genfscon cgroup "/system.slice" -d gen_context(system_u:object_r:cgroup_system_slice_t,s0) fs_use_xattr btrfs gen_context(system_u:object_r:fs_t,s0); fs_use_task eventpollfs gen_context(system_u:object_r:fs_t,s0); bool bool_one; attribute_role rattr; class file { read write open }; roleattribute system_r mount_roles; optional_policy(` ; ') optional_policy(` foo_read(bar_t) ',` # else foo_write(bar_t) ') netifcon lo gen_context(system_u:object_r:lo_netif_t,s0 - mls_systemhigh) gen_context(system_u:object_r:unlabeled_t,s0 - mls_systemhigh) nodecon 127.0.0.1 255.255.255.255 gen_context(system_u:object_r:system_t:s0) nodecon 127.0.0.0/24 gen_context(system_u:object_r:system_t:s0) nodecon ::5 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff gen_context(system_u:object_r:system_t:s0) nodecon ::ffff:127.0.0.1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff gen_context(system_u:object_r:lo_node_t,s0) nodecon ::1/128 gen_context(system_u:object_r:lo_node_t,s0) if (!bool_one) { allow foo_t bar_t:file open; } if (bool_one && ( bool_three || ( bool_four != bool_five))) { allow foo_t baz_t:file write; } if (bool_one == bool_two) { allow foo_t baz_t:file relabelto; } # Interface with a missing comma foo_if(foo_t, bar_t baz_t) allow system_r user_r; empty_interfaces_are_legal() gen_require( type no_backticks_t; role no_backticks_r; ) gen_tunable(`hello',world) gen_tunable(hello,world) define(yes, no) define(`list_of_things', `{ thing1 thing2 }') typebounds foo_t bar_t; call_with_backtick_args(foo_t,bar_t,`in') dollar_sign_in_string("foo1-$3_$2") ifdef(`test',` gen_require(` type nesting_t; ') ') foo_filter_template(synchronizer,synchronizer,`') role_transition foo_r bar_exec_t bar_r; role_transition foo_r baz_exec_t:{file lnk_file sock_file} baz_r; genfscon rootfs / -- gen_context(system_u:object_r:rootfs_t, s0) genfscon rootfs / -l gen_context(system_u:object_r:rootfs_t, s0) #exceptions neverallow { domain -foo_t } secret_stuff_t:file read; empty_string_first(`', foo_t) type type_t alias { alias1 alias2 alias3 }, attribute1; type type_t alias { alias1 alias2 alias3 }, attribute1, attribute2, attribute3; refpolicywarn(`$0($*) has been deprecated, please use foo(); instead.') xperm_macro(foo_t, bar_t, 0x1234) xperm_macro(foo_t, bar_t, { 0x5000 - 0x50ff 1234 }) exempt_interface(foo_t, { -bar1_t -bar2_t }) gen_user(my_user_u,, some_role_r, s0, s0 - mls_systemhigh, mcs_allcats) bare_word bare_word; ifdef(`foo',` bare_word ') ifdef(`foo',` # comment bare_word ') define(`not_foo',`-foo_t') define(`foobar') ifelse(`$1',,, ` refpolicywarn(`dollar one defined') ') example_interface(foo_t, `s0:c0') example_interface(foo_t, `s15:c100.c102') range_transition foo_t foo_t:file s0:c0 - s15:c100.c102; example_interface(foo_t, `s0:c0 - s15:c100.c102') type_transition foo_t foo_t : anon_inode bar_t "[userfaultfd]"; anoninodetrans_pattern(foo_t, bar_t, "[io_uring]") gen_require(` all_userspace_class_perms ') define(`my_var',`') define(`my_other_var',` # This is a comment allow foo_t self:capability sys_ptrace; optional_policy(` call_some_interface(foo_t) ') ') filetrans_pattern(foo_t, bar_run_t, baz_run_t, dir, ``"interface"'') allowxperm src_t tgt_t: netlink_route_socket nlmsg { RTM_GETROUTE 0x44 }; selint-1.5.1/tests/sample_policy_files/with_m4.fc000066400000000000000000000002451475050262500220740ustar00rootroot00000000000000/this/is/a/path -l gen_context(system_u:object_r:abcdefg_t, s0) ifdef(`distro_windows',` /this/is/also/a/path gen_context(system_u:object_r:hijklmn_t, s0) ') selint-1.5.1/tests/test_utils.c000066400000000000000000000052711475050262500165340ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "test_utils.h" struct av_rule_data * make_example_av_rule(void) { // allow foo_t { bar_t baz_t }:file { read write getattr }; struct av_rule_data *av_rule_data = malloc(sizeof(struct av_rule_data)); av_rule_data->flavor = AV_RULE_ALLOW; av_rule_data->sources = calloc(1,sizeof(struct string_list)); ck_assert_ptr_nonnull(av_rule_data->sources); av_rule_data->sources->string = strdup(EXAMPLE_TYPE_1); ck_assert_ptr_nonnull(av_rule_data->sources->string); av_rule_data->sources->next = NULL; av_rule_data->targets = calloc(1,sizeof(struct string_list)); ck_assert_ptr_nonnull(av_rule_data->targets); av_rule_data->targets->string = strdup(EXAMPLE_TYPE_2); ck_assert_ptr_nonnull(av_rule_data->targets->string); av_rule_data->targets->next = calloc(1,sizeof(struct string_list)); ck_assert_ptr_nonnull(av_rule_data->targets->next); av_rule_data->targets->next->string = strdup(EXAMPLE_TYPE_3); ck_assert_ptr_nonnull(av_rule_data->targets->next->string); av_rule_data->targets->next->next = NULL; av_rule_data->object_classes = calloc(1,sizeof(struct string_list)); ck_assert_ptr_nonnull(av_rule_data->object_classes); av_rule_data->object_classes->string = strdup("file"); ck_assert_ptr_nonnull(av_rule_data->object_classes->string); av_rule_data->object_classes->next = NULL; av_rule_data->perms = calloc(1,sizeof(struct string_list)); ck_assert_ptr_nonnull(av_rule_data->perms); av_rule_data->perms->string = strdup("read"); ck_assert_ptr_nonnull(av_rule_data->perms->string); av_rule_data->perms->next = calloc(1,sizeof(struct string_list)); ck_assert_ptr_nonnull(av_rule_data->perms->next); av_rule_data->perms->next->string = strdup("write"); ck_assert_ptr_nonnull(av_rule_data->perms->next->string); av_rule_data->perms->next->next = calloc(1,sizeof(struct string_list)); ck_assert_ptr_nonnull(av_rule_data->perms->next->next); av_rule_data->perms->next->next->string = strdup("getattr"); ck_assert_ptr_nonnull(av_rule_data->perms->next->next->string); av_rule_data->perms->next->next->next = NULL; return av_rule_data; } selint-1.5.1/tests/test_utils.h000066400000000000000000000013721475050262500165370ustar00rootroot00000000000000/* * Copyright 2019 Tresys Technology, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../src/tree.h" #define EXAMPLE_TYPE_1 "foo_t" #define EXAMPLE_TYPE_2 "bar_t" #define EXAMPLE_TYPE_3 "baz_t" struct av_rule_data * make_example_av_rule(void);