diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-09 06:02:45 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-09 06:02:45 +0000 |
commit | a21e897d34d3177da2e720114dd8cf8572fe63e8 (patch) | |
tree | 0ac5f8b919be8431992c69321948d13fdee3ccc6 | |
parent | d6993adcbcafa90a5049ca3f62f279b89b1bb802 (diff) | |
parent | 7ea0a4df6cb4f6c1afb9922c1c6f8a965774b2c2 (diff) | |
download | selinux-android13-frc-extservices-release.tar.gz |
Snap for 8558685 from 7ea0a4df6cb4f6c1afb9922c1c6f8a965774b2c2 to tm-frc-extservices-releaset_frc_ext_330443000android13-frc-extservices-release
Change-Id: I60bdd574afaa66ebbf8b8e1a69bd7f77c24f7531
155 files changed, 3865 insertions, 1293 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 5d3177da..af20484b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,7 @@ jobs: # Install dependencies - run: sudo apt-get update -qq - - run: sudo apt-get install -qq bison clang clang-tools flex gawk gettext libaudit-dev libcap-dev libcap-ng-dev libcunit1-dev libdbus-glib-1-dev libpcre3-dev python3-dev python-dev ruby-dev swig xmlto + - run: sudo apt-get install -qq bison clang clang-tools flex gawk gettext libaudit-dev libcap-dev libcap-ng-dev libcunit1-dev libdbus-glib-1-dev libpcre2-dev python3-dev python-dev ruby-dev swig xmlto - run: name: Setup environment variables diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 5c2233a2..92523db4 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -28,8 +28,9 @@ jobs: uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'selinux' - fuzz-seconds: 180 + fuzz-seconds: 600 dry-run: false + report-unreproducible-crashes: true sanitizer: ${{ matrix.sanitizer }} - name: Upload Crash uses: actions/upload-artifact@v1 diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index ef4be8af..8b7cb720 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -29,6 +29,9 @@ jobs: python-ruby-version: {python: 3.9, ruby: 2.7, other: linker-bfd} - compiler: clang python-ruby-version: {python: 3.9, ruby: 2.7, other: linker-gold} + include: + - compiler: gcc + python-ruby-version: {python: 3.9, ruby: 2.7, other: sanitizers} steps: - uses: actions/checkout@v2 @@ -57,7 +60,7 @@ jobs: libcap-ng-dev \ libcunit1-dev \ libdbus-glib-1-dev \ - libpcre3-dev \ + libpcre2-dev \ python3-dev \ python-dev \ ruby-dev \ @@ -88,6 +91,11 @@ jobs: elif [ "${{ matrix.python-ruby-version.other }}" = "test-debug" ] ; then # Test hat debug build works fine EXPLICIT_MAKE_VARS="DEBUG=1" + elif [ "${{ matrix.python-ruby-version.other }}" = "sanitizers" ] ; then + sanitizers='-fsanitize=address,undefined' + EXPLICIT_MAKE_VARS="CFLAGS='-g -I$DESTDIR/usr/include $sanitizers' LDFLAGS='-L$DESTDIR/usr/lib $sanitizers' LDLIBS= CPPFLAGS= OPT_SUBDIRS=" + echo "ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1" >> $GITHUB_ENV + echo "UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1" >> $GITHUB_ENV else EXPLICIT_MAKE_VARS= fi @@ -139,18 +147,18 @@ jobs: - name: Run tests run: | echo "::group::make install" - make -j$(nproc) install $EXPLICIT_MAKE_VARS -k + eval make -j$(nproc) install $EXPLICIT_MAKE_VARS -k echo "::endgroup::" echo "::group::make install-pywrap" - make -j$(nproc) install-pywrap $EXPLICIT_MAKE_VARS -k + eval make -j$(nproc) install-pywrap $EXPLICIT_MAKE_VARS -k echo "::endgroup::" echo "::group::make install-rubywrap" - make -j$(nproc) install-rubywrap $EXPLICIT_MAKE_VARS -k + eval make -j$(nproc) install-rubywrap $EXPLICIT_MAKE_VARS -k echo "::endgroup::" # Now that everything is installed, run "make all" to build everything which may have not been built echo "::group::make all" - make -j$(nproc) all $EXPLICIT_MAKE_VARS -k + eval make -j$(nproc) all $EXPLICIT_MAKE_VARS -k echo "::endgroup::" # Set up environment variables for the tests and show variables (to help debugging issues) @@ -164,19 +172,21 @@ jobs: # Run tests echo "::group::make test" - make test $EXPLICIT_MAKE_VARS + eval make test $EXPLICIT_MAKE_VARS echo "::endgroup::" - # Test Python and Ruby wrappers - echo "::group::Test Python and Ruby wrappers" - $PYTHON -c 'import selinux;import selinux.audit2why;import semanage;print(selinux.is_selinux_enabled())' - $RUBY -e 'require "selinux";require "semanage";puts Selinux::is_selinux_enabled()' - echo "::endgroup::" - - # Run Python linter, but not on the downloaded refpolicy - echo "::group::scripts/run-flake8" - ./scripts/run-flake8 - echo "::endgroup::" + if [ "${{ matrix.python-ruby-version.other }}" != "sanitizers" ] ; then + # Test Python and Ruby wrappers + echo "::group::Test Python and Ruby wrappers" + $PYTHON -c 'import selinux;import selinux.audit2why;import semanage;print(selinux.is_selinux_enabled())' + $RUBY -e 'require "selinux";require "semanage";puts Selinux::is_selinux_enabled()' + echo "::endgroup::" + + # Run Python linter, but not on the downloaded refpolicy + echo "::group::scripts/run-flake8" + ./scripts/run-flake8 + echo "::endgroup::" + fi echo "::group::Test .gitignore and make clean distclean" # Remove every installed files @@ -184,6 +194,6 @@ jobs: # Test that "git status" looks clean, or print a clear error message git status --short | sed -n 's/^??/error: missing .gitignore entry for/p' | (! grep '^') # Clean up everything and show which file needs to be added to "make clean" - make clean distclean $EXPLICIT_MAKE_VARS + eval make clean distclean $EXPLICIT_MAKE_VARS git ls-files --ignored --others --exclude-standard | sed 's/^/error: "make clean distclean" did not remove /' | (! grep '^') echo "::endgroup::" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a3517cb8..7c548e58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,7 +26,7 @@ using a custom policy please include it as well. There are a number of dependencies required to build the userspace tools/libraries. On a Fedora system you can install them with yum: - # yum install audit-libs-devel bison bzip2-devel dbus-devel dbus-glib-devel flex flex-devel flex-static glib2-devel libcap-devel libcap-ng-devel pam-devel pcre-devel python-devel setools-devel swig ustr-devel + # yum install audit-libs-devel bison bzip2-devel dbus-devel dbus-glib-devel flex flex-devel flex-static glib2-devel libcap-devel libcap-ng-devel pam-devel pcre2-devel python-devel setools-devel swig ustr-devel The tools and libraries can be built and installed under a private directory from the top level with make, e.g. @@ -9,8 +9,12 @@ ifeq ($(DEBUG),1) export LDFLAGS = -g else export CFLAGS ?= -O2 -Werror -Wall -Wextra \ + -Wfloat-equal \ + -Wformat=2 \ + -Winit-self \ -Wmissing-format-attribute \ -Wmissing-noreturn \ + -Wnull-dereference \ -Wpointer-arith \ -Wshadow \ -Wstrict-prototypes \ @@ -51,7 +51,7 @@ dnf install \ libcap-devel \ libcap-ng-devel \ pam-devel \ - pcre-devel \ + pcre2-devel \ xmlto # For Python and Ruby bindings @@ -78,7 +78,7 @@ apt-get install --no-install-recommends --no-install-suggests \ libcap-ng-dev \ libcunit1-dev \ libglib2.0-dev \ - libpcre3-dev \ + libpcre2-dev \ pkgconf \ python3 \ python3-distutils \ diff --git a/checkpolicy/module_compiler.c b/checkpolicy/module_compiler.c index 5f5b0b19..129650fa 100644 --- a/checkpolicy/module_compiler.c +++ b/checkpolicy/module_compiler.c @@ -99,6 +99,7 @@ int define_policy(int pass, int module_header_given) yyerror("no module name"); return -1; } + free(policydbp->name); policydbp->name = id; if ((policydbp->version = queue_remove(id_queue)) == NULL) { diff --git a/checkpolicy/parse_util.c b/checkpolicy/parse_util.c index 8c1f393c..f2d1e04d 100644 --- a/checkpolicy/parse_util.c +++ b/checkpolicy/parse_util.c @@ -47,6 +47,7 @@ int read_source_policy(policydb_t * p, const char *file, const char *progname) } policydbp = p; + policydbp->name = strdup(file); mlspol = p->mls; init_parser(1); diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c index d3eb6111..16b78346 100644 --- a/checkpolicy/policy_define.c +++ b/checkpolicy/policy_define.c @@ -3477,6 +3477,8 @@ static constraint_expr_t *constraint_expr_clone(const constraint_expr_t * expr) return NULL; } +#define PERMISSION_MASK(nprim) ((nprim) == PERM_SYMTAB_SIZE ? (~UINT32_C(0)) : ((UINT32_C(1) << (nprim)) - 1)) + int define_constraint(constraint_expr_t * expr) { struct constraint_node *node; @@ -3590,6 +3592,22 @@ int define_constraint(constraint_expr_t * expr) cladatum = policydbp->class_val_to_struct[i]; node = cladatum->constraints; + if (strcmp(id, "*") == 0) { + node->permissions = PERMISSION_MASK(cladatum->permissions.nprim); + continue; + } + + if (strcmp(id, "~") == 0) { + node->permissions = ~node->permissions & PERMISSION_MASK(cladatum->permissions.nprim); + if (node->permissions == 0) { + yywarn("omitting constraint with no permission set"); + cladatum->constraints = node->next; + constraint_expr_destroy(node->expr); + free(node); + } + continue; + } + perdatum = (perm_datum_t *) hashtab_search(cladatum-> permissions. @@ -5290,6 +5308,14 @@ int define_ipv4_node_context() goto out; } + if (mask.s_addr != 0 && ((~mask.s_addr + 1) & ~mask.s_addr) != 0) { + yywarn("ipv4 mask is not contiguous"); + } + + if ((~mask.s_addr & addr.s_addr) != 0) { + yywarn("host bits in ipv4 address set"); + } + newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); @@ -5325,6 +5351,40 @@ out: return rc; } +static int ipv6_is_mask_contiguous(const struct in6_addr *mask) +{ + int filled = 1; + unsigned i; + + for (i = 0; i < 16; i++) { + if ((((~mask->s6_addr[i] & 0xFF) + 1) & (~mask->s6_addr[i] & 0xFF)) != 0) { + return 0; + } + if (!filled && mask->s6_addr[i] != 0) { + return 0; + } + + if (filled && mask->s6_addr[i] != 0xFF) { + filled = 0; + } + } + + return 1; +} + +static int ipv6_has_host_bits_set(const struct in6_addr *addr, const struct in6_addr *mask) +{ + unsigned i; + + for (i = 0; i < 16; i++) { + if ((addr->s6_addr[i] & ~mask->s6_addr[i]) != 0) { + return 1; + } + } + + return 0; +} + int define_ipv6_node_context(void) { char *id; @@ -5376,6 +5436,14 @@ int define_ipv6_node_context(void) goto out; } + if (!ipv6_is_mask_contiguous(&mask)) { + yywarn("ipv6 mask is not contiguous"); + } + + if (ipv6_has_host_bits_set(&addr, &mask)) { + yywarn("host bits in ipv6 address set"); + } + newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l index 129a8a2a..9fefea7b 100644 --- a/checkpolicy/policy_scan.l +++ b/checkpolicy/policy_scan.l @@ -60,7 +60,14 @@ hexval [0-9A-Fa-f] %% \n.* { +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif strncpy(linebuf[lno], yytext+1, 255); +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif linebuf[lno][254] = 0; lno = 1 - lno; policydb_lineno++; @@ -308,11 +315,11 @@ GLBLUB { return(GLBLUB); } int yyerror(const char *msg) { if (source_file[0]) - fprintf(stderr, "%s:%ld:", + fprintf(stderr, "%s:%lu:", source_file, source_lineno); else fprintf(stderr, "(unknown source)::"); - fprintf(stderr, "ERROR '%s' at token '%s' on line %ld:\n%s\n%s\n", + fprintf(stderr, "ERROR '%s' at token '%s' on line %lu:\n%s\n%s\n", msg, yytext, policydb_lineno, @@ -327,11 +334,11 @@ int yywarn(const char *msg) return yyerror(msg); if (source_file[0]) - fprintf(stderr, "%s:%ld:", + fprintf(stderr, "%s:%lu:", source_file, source_lineno); else fprintf(stderr, "(unknown source)::"); - fprintf(stderr, "WARNING '%s' at token '%s' on line %ld:\n%s\n%s\n", + fprintf(stderr, "WARNING '%s' at token '%s' on line %lu:\n%s\n%s\n", msg, yytext, policydb_lineno, diff --git a/libselinux/Android.bp b/libselinux/Android.bp index 6275b410..fab05ba0 100644 --- a/libselinux/Android.bp +++ b/libselinux/Android.bp @@ -150,9 +150,8 @@ cc_library { ], target: { - linux_glibc: { + host_linux: { srcs: [ - "src/android/android_host.c", "src/avc.c", "src/avc_internal.c", "src/avc_sidtab.c", @@ -162,50 +161,30 @@ cc_library { "src/context.c", "src/deny_unknown.c", "src/enabled.c", - "src/fgetfilecon.c", "src/getenforce.c", "src/getfilecon.c", "src/get_initial_context.c", "src/init.c", - "src/lgetfilecon.c", "src/load_policy.c", - "src/lsetfilecon.c", "src/mapping.c", "src/procattr.c", "src/reject_unknown.c", "src/sestatus.c", - "src/setenforce.c", "src/setexecfilecon.c", - "src/setfilecon.c", "src/stringrep.c", ], }, - linux_bionic: { - enabled: true, + linux_glibc: { srcs: [ - "src/android/android_host.c", - "src/avc.c", - "src/avc_internal.c", - "src/avc_sidtab.c", - "src/compute_av.c", - "src/compute_create.c", - "src/compute_member.c", - "src/context.c", - "src/deny_unknown.c", - "src/enabled.c", - "src/getenforce.c", - "src/getfilecon.c", - "src/get_initial_context.c", - "src/init.c", - "src/load_policy.c", - "src/mapping.c", - "src/procattr.c", - "src/reject_unknown.c", - "src/sestatus.c", - "src/setexecfilecon.c", - "src/stringrep.c", + "src/fgetfilecon.c", + "src/lgetfilecon.c", + "src/lsetfilecon.c", + "src/setfilecon.c", ], }, + linux_bionic: { + enabled: true, + }, android: { srcs: [ diff --git a/libselinux/Makefile b/libselinux/Makefile index 439bc6a9..6d9e2736 100644 --- a/libselinux/Makefile +++ b/libselinux/Makefile @@ -23,7 +23,7 @@ ifeq ($(DISABLE_X11),y) endif export DISABLE_SETRANS DISABLE_RPM DISABLE_FLAGS ANDROID_HOST DISABLE_X11 LABEL_BACKEND_ANDROID -USE_PCRE2 ?= n +USE_PCRE2 ?= y ifeq ($(USE_PCRE2),y) PCRE_MODULE := libpcre2-8 PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8 diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h index 466de39a..1821a3dc 100644 --- a/libselinux/include/selinux/restorecon.h +++ b/libselinux/include/selinux/restorecon.h @@ -2,6 +2,7 @@ #define _RESTORECON_H_ #include <sys/types.h> +#include <stddef.h> #include <stdarg.h> #ifdef __cplusplus @@ -23,6 +24,19 @@ extern "C" { */ extern int selinux_restorecon(const char *pathname, unsigned int restorecon_flags); +/** + * selinux_restorecon_parallel - Relabel files, optionally use more threads. + * @pathname: specifies file/directory to relabel. + * @restorecon_flags: specifies the actions to be performed when relabeling. + * @nthreads: specifies the number of threads to use (0 = use number of CPUs + * currently online) + * + * Same as selinux_restorecon(3), but allows to use multiple threads to do + * the work. + */ +extern int selinux_restorecon_parallel(const char *pathname, + unsigned int restorecon_flags, + size_t nthreads); /* * restorecon_flags options */ diff --git a/libselinux/man/man3/selinux_restorecon.3 b/libselinux/man/man3/selinux_restorecon.3 index ad637406..334d2930 100644 --- a/libselinux/man/man3/selinux_restorecon.3 +++ b/libselinux/man/man3/selinux_restorecon.3 @@ -11,6 +11,14 @@ selinux_restorecon \- restore file(s) default SELinux security contexts .br .BI "unsigned int " restorecon_flags ");" .in +.sp +.BI "int selinux_restorecon_parallel(const char *" pathname , +.in +\w'int selinux_restorecon_parallel('u +.br +.BI "unsigned int " restorecon_flags "," +.br +.BI "size_t " nthreads ");" +.in . .SH "DESCRIPTION" .BR selinux_restorecon () @@ -187,6 +195,27 @@ unless the .B SELINUX_RESTORECON_IGNORE_MOUNTS flag has been set. .RE +.sp +.BR selinux_restorecon_parallel() +is similar to +.BR selinux_restorecon (3), +but accepts another parameter that allows to run relabeling over multiple +threads: +.sp +.RS +.IR nthreads +specifies the number of threads to use during relabeling. When set to 1, +the behavior is the same as calling +.BR selinux_restorecon (3). +When set to 0, the function will try to use as many threads as there are +online CPU cores. When set to any other number, the function will try to use +the given number of threads. +.sp +Note that to use the parallel relabeling capability, the calling process +must be linked with the +.B libpthread +library (either at compile time or dynamically at run time). Otherwise the +function will print a warning and fall back to the single threaded mode. . .SH "RETURN VALUE" On success, zero is returned. On error, \-1 is returned and diff --git a/libselinux/man/man3/selinux_restorecon_parallel.3 b/libselinux/man/man3/selinux_restorecon_parallel.3 new file mode 100644 index 00000000..092d8412 --- /dev/null +++ b/libselinux/man/man3/selinux_restorecon_parallel.3 @@ -0,0 +1 @@ +.so man3/selinux_restorecon.3 diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile index 52c40f01..04bf4f24 100644 --- a/libselinux/src/Makefile +++ b/libselinux/src/Makefile @@ -98,7 +98,6 @@ override LDFLAGS += -L/opt/local/lib -undefined dynamic_lookup LD_SONAME_FLAGS=-install_name,$(LIBSO) endif -PCRE_LDLIBS ?= -lpcre # override with -lfts when building on Musl libc to use fts-standalone FTS_LDLIBS ?= diff --git a/libselinux/src/android/android_host.c b/libselinux/src/android/android_host.c deleted file mode 100644 index 8b55aa78..00000000 --- a/libselinux/src/android/android_host.c +++ /dev/null @@ -1,8 +0,0 @@ -#include <stdlib.h> - -// HACK: placeholder for a library the python bindings expect. -// Delete after b/33170640 is fixed. -const char *selinux_openssh_contexts_path(void) -{ - abort(); -} diff --git a/libselinux/src/android/android_platform.c b/libselinux/src/android/android_platform.c index 2516c091..05c923bc 100644 --- a/libselinux/src/android/android_platform.c +++ b/libselinux/src/android/android_platform.c @@ -806,9 +806,12 @@ static int seapp_context_lookup(enum seapp_kind kind, username = pwd->pw_name; - } else if (appid < AID_ISOLATED_START) { + } else if (appid < AID_SDK_SANDBOX_PROCESS_START) { username = "_app"; appid -= AID_APP; + } else if (appid < AID_ISOLATED_START) { + username = "_sdksandbox"; + appid -= AID_SDK_SANDBOX_PROCESS_START; } else { username = "_isolated"; appid -= AID_ISOLATED_START; @@ -1119,20 +1122,49 @@ struct pkg_info *package_info_lookup(const char *name) * credentials are presented (filenames inside are mangled), so we need * to delay restorecon of those until vold explicitly requests it. */ // NOTE: these paths need to be kept in sync with vold -#define DATA_SYSTEM_CE_PREFIX "/data/system_ce" -#define DATA_VENDOR_CE_PREFIX "/data/vendor_ce" -#define DATA_MISC_CE_PREFIX "/data/misc_ce" +#define DATA_SYSTEM_CE_PATH "/data/system_ce" +#define DATA_VENDOR_CE_PATH "/data/vendor_ce" +#define DATA_MISC_CE_PATH "/data/misc_ce" +#define DATA_MISC_DE_PATH "/data/misc_de" /* The path prefixes of package data directories. */ #define DATA_DATA_PATH "/data/data" #define DATA_USER_PATH "/data/user" #define DATA_USER_DE_PATH "/data/user_de" -#define EXPAND_USER_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user" -#define EXPAND_USER_DE_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user_de" #define USER_PROFILE_PATH "/data/misc/profiles/cur/*" +#define SDK_SANDBOX_DATA_CE_PATH "/data/misc_ce/*/sdksandbox" +#define SDK_SANDBOX_DATA_DE_PATH "/data/misc_de/*/sdksandbox" + +#define EXPAND_MNT_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?" +#define EXPAND_USER_PATH EXPAND_MNT_PATH "/user" +#define EXPAND_USER_DE_PATH EXPAND_MNT_PATH "/user_de" +#define EXPAND_SDK_CE_PATH EXPAND_MNT_PATH "/misc_ce/*/sdksandbox" +#define EXPAND_SDK_DE_PATH EXPAND_MNT_PATH "/misc_de/*/sdksandbox" + #define DATA_DATA_PREFIX DATA_DATA_PATH "/" #define DATA_USER_PREFIX DATA_USER_PATH "/" #define DATA_USER_DE_PREFIX DATA_USER_DE_PATH "/" +#define DATA_MISC_CE_PREFIX DATA_MISC_CE_PATH "/" +#define DATA_MISC_DE_PREFIX DATA_MISC_DE_PATH "/" +#define EXPAND_MNT_PATH_PREFIX EXPAND_MNT_PATH "/" + +/* + * This method helps in identifying paths that refer to users' app data. Labeling for app data is + * based on seapp_contexts and seinfo assignments rather than file_contexts and is managed by + * installd rather than by init. + */ +static bool is_app_data_path(const char *pathname) { + int flags = FNM_LEADING_DIR|FNM_PATHNAME; + return (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) || + !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) || + !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) || + !fnmatch(EXPAND_USER_PATH, pathname, flags) || + !fnmatch(EXPAND_USER_DE_PATH, pathname, flags) || + !fnmatch(SDK_SANDBOX_DATA_CE_PATH, pathname, flags) || + !fnmatch(SDK_SANDBOX_DATA_DE_PATH, pathname, flags) || + !fnmatch(EXPAND_SDK_CE_PATH, pathname, flags) || + !fnmatch(EXPAND_SDK_DE_PATH, pathname, flags)); +} static int pkgdir_selabel_lookup(const char *pathname, const char *seinfo, @@ -1180,6 +1212,40 @@ static int pkgdir_selabel_lookup(const char *pathname, pathname++; else return 0; + } else if (!strncmp(pathname, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1)) { + pathname += sizeof(DATA_MISC_CE_PREFIX) - 1; + while (isdigit(*pathname)) + pathname++; + if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) { + pathname += sizeof("/sdksandbox/") - 1; + } else + return 0; + } else if (!strncmp(pathname, DATA_MISC_DE_PREFIX, sizeof(DATA_MISC_DE_PREFIX)-1)) { + pathname += sizeof(DATA_MISC_DE_PREFIX) - 1; + while (isdigit(*pathname)) + pathname++; + if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) { + pathname += sizeof("/sdksandbox/") - 1; + } else + return 0; + } else if (!fnmatch(EXPAND_SDK_CE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) { + pathname += sizeof(EXPAND_MNT_PATH_PREFIX) - 1; + pathname += sizeof("misc_ce/") - 1; + while (isdigit(*pathname)) + pathname++; + if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) { + pathname += sizeof("/sdksandbox/") - 1; + } else + return 0; + } else if (!fnmatch(EXPAND_SDK_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) { + pathname += sizeof(EXPAND_MNT_PATH_PREFIX) - 1; + pathname += sizeof("misc_de/") - 1; + while (isdigit(*pathname)) + pathname++; + if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) { + pathname += sizeof("/sdksandbox/") - 1; + } else + return 0; } else return 0; @@ -1268,11 +1334,7 @@ static int restorecon_sb(const char *pathname, const struct stat *sb, * have different labeling rules, based off of /seapp_contexts, and * installd is responsible for managing these labels instead of init. */ - if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) || - !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) || - !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) || - !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) || - !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) { + if (is_app_data_path(pathname)) { if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0) goto err; } @@ -1435,11 +1497,7 @@ static int selinux_android_restorecon_common(const char* pathname_orig, * assignments rather than file_contexts and is managed by * installd rather than init. */ - if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) || - !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) || - !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) || - !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) || - !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) + if (is_app_data_path(pathname)) setrestoreconlast = false; /* Also ignore on /sys since it is regenerated on each boot regardless. */ @@ -1516,20 +1574,15 @@ static int selinux_android_restorecon_common(const char* pathname_orig, } if (skipce && - (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PREFIX, sizeof(DATA_SYSTEM_CE_PREFIX)-1) || - !strncmp(ftsent->fts_path, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1) || - !strncmp(ftsent->fts_path, DATA_VENDOR_CE_PREFIX, sizeof(DATA_VENDOR_CE_PREFIX)-1))) { + (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PATH, sizeof(DATA_SYSTEM_CE_PATH)-1) || + !strncmp(ftsent->fts_path, DATA_MISC_CE_PATH, sizeof(DATA_MISC_CE_PATH)-1) || + !strncmp(ftsent->fts_path, DATA_VENDOR_CE_PATH, sizeof(DATA_VENDOR_CE_PATH)-1))) { // Don't label anything below this directory. fts_set(fts, ftsent, FTS_SKIP); // but fall through and make sure we label the directory itself } - if (!datadata && - (!strcmp(ftsent->fts_path, DATA_DATA_PATH) || - !strncmp(ftsent->fts_path, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) || - !strncmp(ftsent->fts_path, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) || - !fnmatch(EXPAND_USER_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME) || - !fnmatch(EXPAND_USER_DE_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME))) { + if (!datadata && is_app_data_path(ftsent->fts_path)) { // Don't label anything below this directory. fts_set(fts, ftsent, FTS_SKIP); // but fall through and make sure we label the directory itself diff --git a/libselinux/src/callbacks.c b/libselinux/src/callbacks.c index c18ccc54..469c4055 100644 --- a/libselinux/src/callbacks.c +++ b/libselinux/src/callbacks.c @@ -10,6 +10,8 @@ #include <selinux/selinux.h> #include "callbacks.h" +pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; + /* default implementations */ static int __attribute__ ((format(printf, 2, 3))) default_selinux_log(int type __attribute__((unused)), const char *fmt, ...) @@ -56,7 +58,7 @@ default_selinux_policyload(int seqno __attribute__((unused))) /* callback pointers */ int __attribute__ ((format(printf, 2, 3))) -(*selinux_log)(int, const char *, ...) = +(*selinux_log_direct)(int, const char *, ...) = default_selinux_log; int @@ -81,7 +83,7 @@ selinux_set_callback(int type, union selinux_callback cb) { switch (type) { case SELINUX_CB_LOG: - selinux_log = cb.func_log; + selinux_log_direct = cb.func_log; break; case SELINUX_CB_AUDIT: selinux_audit = cb.func_audit; @@ -106,7 +108,7 @@ selinux_get_callback(int type) switch (type) { case SELINUX_CB_LOG: - cb.func_log = selinux_log; + cb.func_log = selinux_log_direct; break; case SELINUX_CB_AUDIT: cb.func_audit = selinux_audit; diff --git a/libselinux/src/callbacks.h b/libselinux/src/callbacks.h index 03d87f0c..f4dab157 100644 --- a/libselinux/src/callbacks.h +++ b/libselinux/src/callbacks.h @@ -10,9 +10,11 @@ #include <string.h> #include <selinux/selinux.h> +#include "selinux_internal.h" + /* callback pointers */ extern int __attribute__ ((format(printf, 2, 3))) -(*selinux_log) (int type, const char *, ...) ; +(*selinux_log_direct) (int type, const char *, ...) ; extern int (*selinux_audit) (void *, security_class_t, char *, size_t) ; @@ -26,4 +28,13 @@ extern int extern int (*selinux_netlink_policyload) (int seqno) ; +/* Thread-safe selinux_log() function */ +extern pthread_mutex_t log_mutex; + +#define selinux_log(type, ...) do { \ + __pthread_mutex_lock(&log_mutex); \ + selinux_log_direct(type, __VA_ARGS__); \ + __pthread_mutex_unlock(&log_mutex); \ +} while(0) + #endif /* _SELINUX_CALLBACKS_H_ */ diff --git a/libselinux/src/is_customizable_type.c b/libselinux/src/is_customizable_type.c index 1b17860c..f83e1e83 100644 --- a/libselinux/src/is_customizable_type.c +++ b/libselinux/src/is_customizable_type.c @@ -9,7 +9,10 @@ #include "selinux_internal.h" #include "context_internal.h" -static int get_customizable_type_list(char *** retlist) +static char **customizable_list = NULL; +static pthread_once_t customizable_once = PTHREAD_ONCE_INIT; + +static void customizable_init(void) { FILE *fp; char *buf; @@ -18,12 +21,12 @@ static int get_customizable_type_list(char *** retlist) fp = fopen(selinux_customizable_types_path(), "re"); if (!fp) - return -1; + return; buf = malloc(selinux_page_size); if (!buf) { fclose(fp); - return -1; + return; } while (fgets_unlocked(buf, selinux_page_size, fp) && ctr < UINT_MAX) { ctr++; @@ -54,23 +57,19 @@ static int get_customizable_type_list(char *** retlist) fclose(fp); free(buf); if (!list) - return -1; - *retlist = list; - return 0; + return; + customizable_list = list; } -static char **customizable_list = NULL; - int is_context_customizable(const char * scontext) { int i; const char *type; context_t c; - if (!customizable_list) { - if (get_customizable_type_list(&customizable_list) != 0) - return -1; - } + __selinux_once(customizable_once, customizable_init); + if (!customizable_list) + return -1; c = context_new(scontext); if (!c) diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c index 70ea86fa..695d6a9e 100644 --- a/libselinux/src/label_file.c +++ b/libselinux/src/label_file.c @@ -997,7 +997,12 @@ static struct spec **lookup_all(struct selabel_handle *rec, rc = regex_match(spec->regex, key, partial); if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) { if (rc == REGEX_MATCH) { - spec->matches++; +#ifdef __ATOMIC_RELAXED + __atomic_store_n(&spec->any_matches, + true, __ATOMIC_RELAXED); +#else +#error "Please use a compiler that supports __atomic builtins" +#endif } if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) { @@ -1295,9 +1300,15 @@ static void stats(struct selabel_handle *rec) struct saved_data *data = (struct saved_data *)rec->data; unsigned int i, nspec = data->nspec; struct spec *spec_arr = data->spec_arr; + bool any_matches; for (i = 0; i < nspec; i++) { - if (spec_arr[i].matches == 0) { +#ifdef __ATOMIC_RELAXED + any_matches = __atomic_load_n(&spec_arr[i].any_matches, __ATOMIC_RELAXED); +#else +#error "Please use a compiler that supports __atomic builtins" +#endif + if (!any_matches) { if (spec_arr[i].type_str) { COMPAT_LOG(SELINUX_WARNING, "Warning! No matches for (%s, %s, %s)\n", diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h index 343ffc70..b453e13f 100644 --- a/libselinux/src/label_file.h +++ b/libselinux/src/label_file.h @@ -51,7 +51,7 @@ struct spec { bool regex_compiled; /* bool to indicate if the regex is compiled */ pthread_mutex_t regex_lock; /* lock for lazy compilation of regex */ mode_t mode; /* mode format value */ - int matches; /* number of matching pathnames */ + bool any_matches; /* did any pathname match? */ int stem_id; /* indicates which stem-compression item */ char hasMetaChars; /* regular expression has meta-chars */ char from_mmap; /* this spec is from an mmap of the data */ diff --git a/libselinux/src/libselinux.map b/libselinux/src/libselinux.map index 2a368e93..4acf1caa 100644 --- a/libselinux/src/libselinux.map +++ b/libselinux/src/libselinux.map @@ -240,3 +240,8 @@ LIBSELINUX_1.0 { local: *; }; + +LIBSELINUX_3.4 { + global: + selinux_restorecon_parallel; +} LIBSELINUX_1.0; diff --git a/libselinux/src/matchpathcon.c b/libselinux/src/matchpathcon.c index 1e7f8890..ea78a23e 100644 --- a/libselinux/src/matchpathcon.c +++ b/libselinux/src/matchpathcon.c @@ -356,7 +356,7 @@ int matchpathcon_init_prefix(const char *path, const char *subset) mycanoncon = default_canoncon; __selinux_once(once, matchpathcon_init_once); - __selinux_setspecific(destructor_key, (void *)1); + __selinux_setspecific(destructor_key, /* some valid address to please GCC */ &selinux_page_size); options[SELABEL_OPT_SUBSET].type = SELABEL_OPT_SUBSET; options[SELABEL_OPT_SUBSET].value = subset; diff --git a/libselinux/src/procattr.c b/libselinux/src/procattr.c index 6552ee01..142fbf3a 100644 --- a/libselinux/src/procattr.c +++ b/libselinux/src/procattr.c @@ -68,7 +68,7 @@ void __attribute__((destructor)) procattr_destructor(void) static inline void init_thread_destructor(void) { if (destructor_initialized == 0) { - __selinux_setspecific(destructor_key, (void *)1); + __selinux_setspecific(destructor_key, /* some valid address to please GCC */ &selinux_page_size); destructor_initialized = 1; } } diff --git a/libselinux/src/selinux_config.c b/libselinux/src/selinux_config.c index 97f81a8b..d2e49ee1 100644 --- a/libselinux/src/selinux_config.c +++ b/libselinux/src/selinux_config.c @@ -92,6 +92,7 @@ int selinux_getenforcemode(int *enforce) FILE *cfg = fopen(SELINUXCONFIG, "re"); if (cfg) { char *buf; + char *tag; int len = sizeof(SELINUXTAG) - 1; buf = malloc(selinux_page_size); if (!buf) { @@ -101,21 +102,24 @@ int selinux_getenforcemode(int *enforce) while (fgets_unlocked(buf, selinux_page_size, cfg)) { if (strncmp(buf, SELINUXTAG, len)) continue; + tag = buf+len; + while (isspace(*tag)) + tag++; if (!strncasecmp - (buf + len, "enforcing", sizeof("enforcing") - 1)) { + (tag, "enforcing", sizeof("enforcing") - 1)) { *enforce = 1; ret = 0; break; } else if (!strncasecmp - (buf + len, "permissive", + (tag, "permissive", sizeof("permissive") - 1)) { *enforce = 0; ret = 0; break; } else if (!strncasecmp - (buf + len, "disabled", + (tag, "disabled", sizeof("disabled") - 1)) { *enforce = -1; ret = 0; @@ -176,7 +180,10 @@ static void init_selinux_config(void) if (!strncasecmp(buf_p, SELINUXTYPETAG, sizeof(SELINUXTYPETAG) - 1)) { - type = strdup(buf_p + sizeof(SELINUXTYPETAG) - 1); + buf_p += sizeof(SELINUXTYPETAG) - 1; + while (isspace(*buf_p)) + buf_p++; + type = strdup(buf_p); if (!type) { free(line_buf); fclose(fp); @@ -199,6 +206,8 @@ static void init_selinux_config(void) } else if (!strncmp(buf_p, REQUIRESEUSERS, sizeof(REQUIRESEUSERS) - 1)) { value = buf_p + sizeof(REQUIRESEUSERS) - 1; + while (isspace(*value)) + value++; intptr = &require_seusers; } else { continue; diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h index 27e9ac53..297dcf26 100644 --- a/libselinux/src/selinux_internal.h +++ b/libselinux/src/selinux_internal.h @@ -69,6 +69,22 @@ extern int selinux_page_size ; pthread_mutex_unlock(LOCK); \ } while (0) +#pragma weak pthread_create +#pragma weak pthread_join +#pragma weak pthread_cond_init +#pragma weak pthread_cond_signal +#pragma weak pthread_cond_destroy +#pragma weak pthread_cond_wait + +/* check if all functions needed to do parallel operations are available */ +#define __pthread_supported ( \ + pthread_create && \ + pthread_join && \ + pthread_cond_init && \ + pthread_cond_destroy && \ + pthread_cond_signal && \ + pthread_cond_wait \ +) #define SELINUXDIR "/etc/selinux/" #define SELINUXCONFIG SELINUXDIR "config" diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c index 04d95650..72f4fb46 100644 --- a/libselinux/src/selinux_restorecon.c +++ b/libselinux/src/selinux_restorecon.c @@ -60,6 +60,7 @@ static int exclude_count = 0; static struct edir *exclude_lst = NULL; static uint64_t fc_count = 0; /* Number of files processed so far */ static uint64_t efile_count; /* Estimated total number of files */ +static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; /* Store information on directories with xattr's. */ static struct dir_xattr *dir_xattr_list; @@ -411,6 +412,7 @@ typedef struct file_spec { } file_spec_t; static file_spec_t *fl_head; +static pthread_mutex_t fl_mutex = PTHREAD_MUTEX_INITIALIZER; /* * Try to add an association between an inode and a context. If there is a @@ -424,11 +426,12 @@ static int filespec_add(ino_t ino, const char *con, const char *file, int h, ret; struct stat64 sb; + __pthread_mutex_lock(&fl_mutex); + if (!fl_head) { - fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS); + fl_head = calloc(HASH_BUCKETS, sizeof(file_spec_t)); if (!fl_head) goto oom; - memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS); } h = (ino + (ino >> HASH_BITS)) & HASH_MASK; @@ -445,11 +448,11 @@ static int filespec_add(ino_t ino, const char *con, const char *file, fl->con = strdup(con); if (!fl->con) goto oom; - return 1; + goto unlock_1; } if (strcmp(fl->con, con) == 0) - return 1; + goto unlock_1; selinux_log(SELINUX_ERROR, "conflicting specifications for %s and %s, using %s.\n", @@ -458,6 +461,9 @@ static int filespec_add(ino_t ino, const char *con, const char *file, fl->file = strdup(file); if (!fl->file) goto oom; + + __pthread_mutex_unlock(&fl_mutex); + if (flags->conflicterror) { selinux_log(SELINUX_ERROR, "treating conflicting specifications as an error.\n"); @@ -482,13 +488,19 @@ static int filespec_add(ino_t ino, const char *con, const char *file, goto oom_freefl; fl->next = prevfl->next; prevfl->next = fl; + + __pthread_mutex_unlock(&fl_mutex); return 0; oom_freefl: free(fl); oom: + __pthread_mutex_unlock(&fl_mutex); selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); return -1; +unlock_1: + __pthread_mutex_unlock(&fl_mutex); + return 1; } /* @@ -598,7 +610,7 @@ out: } static int restorecon_sb(const char *pathname, const struct stat *sb, - struct rest_flags *flags) + struct rest_flags *flags, bool first) { char *newcon = NULL; char *curcon = NULL; @@ -627,7 +639,7 @@ static int restorecon_sb(const char *pathname, const struct stat *sb, sb->st_mode); if (rc < 0) { - if (errno == ENOENT && flags->warnonnomatch) + if (errno == ENOENT && flags->warnonnomatch && first) selinux_log(SELINUX_INFO, "Warning no default label for %s\n", lookup_path); @@ -636,6 +648,7 @@ static int restorecon_sb(const char *pathname, const struct stat *sb, } if (flags->progress) { + __pthread_mutex_lock(&progress_mutex); fc_count++; if (fc_count % STAR_COUNT == 0) { if (flags->mass_relabel && efile_count > 0) { @@ -647,6 +660,7 @@ static int restorecon_sb(const char *pathname, const struct stat *sb, } fflush(stdout); } + __pthread_mutex_unlock(&progress_mutex); } if (flags->add_assoc) { @@ -800,66 +814,215 @@ oom: goto free; } +struct rest_state { + struct rest_flags flags; + dev_t dev_num; + struct statfs sfsb; + bool ignore_digest; + bool setrestorecondigest; + bool parallel; -/* - * Public API - */ + FTS *fts; + FTSENT *ftsent_first; + struct dir_hash_node *head, *current; + bool abort; + int error; + int saved_errno; + pthread_mutex_t mutex; +}; -/* selinux_restorecon(3) - Main function that is responsible for labeling */ -int selinux_restorecon(const char *pathname_orig, - unsigned int restorecon_flags) +static void *selinux_restorecon_thread(void *arg) { - struct rest_flags flags; + struct rest_state *state = arg; + FTS *fts = state->fts; + FTSENT *ftsent; + int error; + char ent_path[PATH_MAX]; + struct stat ent_st; + bool first = false; + + if (state->parallel) + pthread_mutex_lock(&state->mutex); + + if (state->ftsent_first) { + ftsent = state->ftsent_first; + state->ftsent_first = NULL; + first = true; + goto loop_body; + } + + while (((void)(errno = 0), ftsent = fts_read(fts)) != NULL) { +loop_body: + /* If the FTS_XDEV flag is set and the device is different */ + if (state->flags.set_xdev && + ftsent->fts_statp->st_dev != state->dev_num) + continue; - flags.nochange = (restorecon_flags & + switch (ftsent->fts_info) { + case FTS_DC: + selinux_log(SELINUX_ERROR, + "Directory cycle on %s.\n", + ftsent->fts_path); + errno = ELOOP; + state->error = -1; + state->abort = true; + goto finish; + case FTS_DP: + continue; + case FTS_DNR: + error = errno; + errno = ftsent->fts_errno; + selinux_log(SELINUX_ERROR, + "Could not read %s: %m.\n", + ftsent->fts_path); + errno = error; + fts_set(fts, ftsent, FTS_SKIP); + continue; + case FTS_NS: + error = errno; + errno = ftsent->fts_errno; + selinux_log(SELINUX_ERROR, + "Could not stat %s: %m.\n", + ftsent->fts_path); + errno = error; + fts_set(fts, ftsent, FTS_SKIP); + continue; + case FTS_ERR: + error = errno; + errno = ftsent->fts_errno; + selinux_log(SELINUX_ERROR, + "Error on %s: %m.\n", + ftsent->fts_path); + errno = error; + fts_set(fts, ftsent, FTS_SKIP); + continue; + case FTS_D: + if (state->sfsb.f_type == SYSFS_MAGIC && + !selabel_partial_match(fc_sehandle, + ftsent->fts_path)) { + fts_set(fts, ftsent, FTS_SKIP); + continue; + } + + if (check_excluded(ftsent->fts_path)) { + fts_set(fts, ftsent, FTS_SKIP); + continue; + } + + if (state->setrestorecondigest) { + struct dir_hash_node *new_node = NULL; + + if (check_context_match_for_dir(ftsent->fts_path, + &new_node, + state->error) && + !state->ignore_digest) { + selinux_log(SELINUX_INFO, + "Skipping restorecon on directory(%s)\n", + ftsent->fts_path); + fts_set(fts, ftsent, FTS_SKIP); + continue; + } + + if (new_node && !state->error) { + if (!state->current) { + state->current = new_node; + state->head = state->current; + } else { + state->current->next = new_node; + state->current = new_node; + } + } + } + /* fall through */ + default: + strcpy(ent_path, ftsent->fts_path); + ent_st = *ftsent->fts_statp; + if (state->parallel) + pthread_mutex_unlock(&state->mutex); + + error = restorecon_sb(ent_path, &ent_st, &state->flags, + first); + + if (state->parallel) { + pthread_mutex_lock(&state->mutex); + if (state->abort) + goto unlock; + } + + state->error |= error; + first = false; + if (error && state->flags.abort_on_error) { + state->abort = true; + goto finish; + } + break; + } + } + +finish: + if (!state->saved_errno) + state->saved_errno = errno; +unlock: + if (state->parallel) + pthread_mutex_unlock(&state->mutex); + return NULL; +} + +static int selinux_restorecon_common(const char *pathname_orig, + unsigned int restorecon_flags, + size_t nthreads) +{ + struct rest_state state; + + state.flags.nochange = (restorecon_flags & SELINUX_RESTORECON_NOCHANGE) ? true : false; - flags.verbose = (restorecon_flags & + state.flags.verbose = (restorecon_flags & SELINUX_RESTORECON_VERBOSE) ? true : false; - flags.progress = (restorecon_flags & + state.flags.progress = (restorecon_flags & SELINUX_RESTORECON_PROGRESS) ? true : false; - flags.mass_relabel = (restorecon_flags & + state.flags.mass_relabel = (restorecon_flags & SELINUX_RESTORECON_MASS_RELABEL) ? true : false; - flags.recurse = (restorecon_flags & + state.flags.recurse = (restorecon_flags & SELINUX_RESTORECON_RECURSE) ? true : false; - flags.set_specctx = (restorecon_flags & + state.flags.set_specctx = (restorecon_flags & SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false; - flags.userealpath = (restorecon_flags & + state.flags.userealpath = (restorecon_flags & SELINUX_RESTORECON_REALPATH) ? true : false; - flags.set_xdev = (restorecon_flags & + state.flags.set_xdev = (restorecon_flags & SELINUX_RESTORECON_XDEV) ? true : false; - flags.add_assoc = (restorecon_flags & + state.flags.add_assoc = (restorecon_flags & SELINUX_RESTORECON_ADD_ASSOC) ? true : false; - flags.abort_on_error = (restorecon_flags & + state.flags.abort_on_error = (restorecon_flags & SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false; - flags.syslog_changes = (restorecon_flags & + state.flags.syslog_changes = (restorecon_flags & SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false; - flags.log_matches = (restorecon_flags & + state.flags.log_matches = (restorecon_flags & SELINUX_RESTORECON_LOG_MATCHES) ? true : false; - flags.ignore_noent = (restorecon_flags & + state.flags.ignore_noent = (restorecon_flags & SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false; - flags.warnonnomatch = true; - flags.conflicterror = (restorecon_flags & + state.flags.warnonnomatch = true; + state.flags.conflicterror = (restorecon_flags & SELINUX_RESTORECON_CONFLICT_ERROR) ? true : false; ignore_mounts = (restorecon_flags & SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false; - bool ignore_digest = (restorecon_flags & + state.ignore_digest = (restorecon_flags & SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false; - bool setrestorecondigest = true; + state.setrestorecondigest = true; + + state.head = NULL; + state.current = NULL; + state.abort = false; + state.error = 0; + state.saved_errno = 0; struct stat sb; - struct statfs sfsb; - FTS *fts; - FTSENT *ftsent; char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname; char *paths[2] = { NULL, NULL }; int fts_flags, error, sverrno; - dev_t dev_num = 0; struct dir_hash_node *current = NULL; - struct dir_hash_node *head = NULL; - int errno_tmp; - if (flags.verbose && flags.progress) - flags.verbose = false; + if (state.flags.verbose && state.flags.progress) + state.flags.verbose = false; __selinux_once(fc_once, restorecon_init); @@ -872,13 +1035,31 @@ int selinux_restorecon(const char *pathname_orig, */ if (selabel_no_digest || (restorecon_flags & SELINUX_RESTORECON_SKIP_DIGEST)) - setrestorecondigest = false; + state.setrestorecondigest = false; + + if (!__pthread_supported) { + if (nthreads != 1) { + nthreads = 1; + selinux_log(SELINUX_WARNING, + "Threading functionality not available, falling back to 1 thread."); + } + } else if (nthreads == 0) { + long nproc = sysconf(_SC_NPROCESSORS_ONLN); + + if (nproc > 0) { + nthreads = nproc; + } else { + nthreads = 1; + selinux_log(SELINUX_WARNING, + "Unable to detect CPU count, falling back to 1 thread."); + } + } /* * Convert passed-in pathname to canonical pathname by resolving * realpath of containing dir, then appending last component name. */ - if (flags.userealpath) { + if (state.flags.userealpath) { char *basename_cpy = strdup(pathname_orig); if (!basename_cpy) goto realpatherr; @@ -923,7 +1104,7 @@ int selinux_restorecon(const char *pathname_orig, paths[0] = pathname; if (lstat(pathname, &sb) < 0) { - if (flags.ignore_noent && errno == ENOENT) { + if (state.flags.ignore_noent && errno == ENOENT) { free(pathdnamer); free(pathname); return 0; @@ -938,21 +1119,21 @@ int selinux_restorecon(const char *pathname_orig, /* Skip digest if not a directory */ if (!S_ISDIR(sb.st_mode)) - setrestorecondigest = false; + state.setrestorecondigest = false; - if (!flags.recurse) { + if (!state.flags.recurse) { if (check_excluded(pathname)) { error = 0; goto cleanup; } - error = restorecon_sb(pathname, &sb, &flags); + error = restorecon_sb(pathname, &sb, &state.flags, true); goto cleanup; } /* Obtain fs type */ - memset(&sfsb, 0, sizeof sfsb); - if (!S_ISLNK(sb.st_mode) && statfs(pathname, &sfsb) < 0) { + memset(&state.sfsb, 0, sizeof(state.sfsb)); + if (!S_ISLNK(sb.st_mode) && statfs(pathname, &state.sfsb) < 0) { selinux_log(SELINUX_ERROR, "statfs(%s) failed: %m\n", pathname); @@ -961,21 +1142,21 @@ int selinux_restorecon(const char *pathname_orig, } /* Skip digest on in-memory filesystems and /sys */ - if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC || - sfsb.f_type == SYSFS_MAGIC) - setrestorecondigest = false; + if (state.sfsb.f_type == RAMFS_MAGIC || state.sfsb.f_type == TMPFS_MAGIC || + state.sfsb.f_type == SYSFS_MAGIC) + state.setrestorecondigest = false; - if (flags.set_xdev) + if (state.flags.set_xdev) fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV; else fts_flags = FTS_PHYSICAL | FTS_NOCHDIR; - fts = fts_open(paths, fts_flags, NULL); - if (!fts) + state.fts = fts_open(paths, fts_flags, NULL); + if (!state.fts) goto fts_err; - ftsent = fts_read(fts); - if (!ftsent) + state.ftsent_first = fts_read(state.fts); + if (!state.ftsent_first) goto fts_err; /* @@ -987,106 +1168,66 @@ int selinux_restorecon(const char *pathname_orig, * directories with a different device number when the FTS_XDEV flag * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2). */ - dev_num = ftsent->fts_statp->st_dev; + state.dev_num = state.ftsent_first->fts_statp->st_dev; - error = 0; - do { - /* If the FTS_XDEV flag is set and the device is different */ - if (flags.set_xdev && ftsent->fts_statp->st_dev != dev_num) - continue; + if (nthreads == 1) { + state.parallel = false; + selinux_restorecon_thread(&state); + } else { + size_t i; + pthread_t self = pthread_self(); + pthread_t *threads = NULL; - switch (ftsent->fts_info) { - case FTS_DC: - selinux_log(SELINUX_ERROR, - "Directory cycle on %s.\n", - ftsent->fts_path); - errno = ELOOP; - error = -1; - goto out; - case FTS_DP: - continue; - case FTS_DNR: - errno_tmp = errno; - errno = ftsent->fts_errno; - selinux_log(SELINUX_ERROR, - "Could not read %s: %m.\n", - ftsent->fts_path); - errno = errno_tmp; - fts_set(fts, ftsent, FTS_SKIP); - continue; - case FTS_NS: - errno_tmp = errno; - errno = ftsent->fts_errno; - selinux_log(SELINUX_ERROR, - "Could not stat %s: %m.\n", - ftsent->fts_path); - errno = errno_tmp; - fts_set(fts, ftsent, FTS_SKIP); - continue; - case FTS_ERR: - errno_tmp = errno; - errno = ftsent->fts_errno; - selinux_log(SELINUX_ERROR, - "Error on %s: %m.\n", - ftsent->fts_path); - errno = errno_tmp; - fts_set(fts, ftsent, FTS_SKIP); - continue; - case FTS_D: - if (sfsb.f_type == SYSFS_MAGIC && - !selabel_partial_match(fc_sehandle, - ftsent->fts_path)) { - fts_set(fts, ftsent, FTS_SKIP); - continue; - } + pthread_mutex_init(&state.mutex, NULL); - if (check_excluded(ftsent->fts_path)) { - fts_set(fts, ftsent, FTS_SKIP); - continue; + threads = calloc(nthreads - 1, sizeof(*threads)); + if (!threads) + goto oom; + + state.parallel = true; + /* + * Start (nthreads - 1) threads - the main thread is going to + * take part, too. + */ + for (i = 0; i < nthreads - 1; i++) { + if (pthread_create(&threads[i], NULL, + selinux_restorecon_thread, &state)) { + /* + * If any thread fails to be created, just mark + * it as such and let the successfully created + * threads do the job. In the worst case the + * main thread will do everything, but that's + * still better than to give up. + */ + threads[i] = self; } + } - if (setrestorecondigest) { - struct dir_hash_node *new_node = NULL; + /* Let's join in on the fun! */ + selinux_restorecon_thread(&state); - if (check_context_match_for_dir(ftsent->fts_path, - &new_node, - error) && - !ignore_digest) { - selinux_log(SELINUX_INFO, - "Skipping restorecon on directory(%s)\n", - ftsent->fts_path); - fts_set(fts, ftsent, FTS_SKIP); - continue; - } - - if (new_node && !error) { - if (!current) { - current = new_node; - head = current; - } else { - current->next = new_node; - current = current->next; - } - } - } - /* fall through */ - default: - error |= restorecon_sb(ftsent->fts_path, - ftsent->fts_statp, &flags); - if (flags.warnonnomatch) - flags.warnonnomatch = false; - if (error && flags.abort_on_error) - goto out; - break; + /* Now wait for all threads to finish. */ + for (i = 0; i < nthreads - 1; i++) { + /* Skip threads that failed to be created. */ + if (pthread_equal(threads[i], self)) + continue; + pthread_join(threads[i], NULL); } - } while ((ftsent = fts_read(fts)) != NULL); + free(threads); + + pthread_mutex_destroy(&state.mutex); + } + + error = state.error; + if (state.saved_errno) + goto out; /* * Labeling successful. Write partial match digests for subdirectories. * TODO: Write digest upon FTS_DP if no error occurs in its descents. */ - if (setrestorecondigest && !flags.nochange && !error) { - current = head; + if (state.setrestorecondigest && !state.flags.nochange && !error) { + current = state.head; while (current != NULL) { if (setxattr(current->path, RESTORECON_PARTIAL_MATCH_DIGEST, @@ -1101,22 +1242,21 @@ int selinux_restorecon(const char *pathname_orig, } out: - if (flags.progress && flags.mass_relabel) + if (state.flags.progress && state.flags.mass_relabel) fprintf(stdout, "\r%s 100.0%%\n", pathname); - sverrno = errno; - (void) fts_close(fts); - errno = sverrno; + (void) fts_close(state.fts); + errno = state.saved_errno; cleanup: - if (flags.add_assoc) { - if (flags.verbose) + if (state.flags.add_assoc) { + if (state.flags.verbose) filespec_eval(); filespec_destroy(); } free(pathdnamer); free(pathname); - current = head; + current = state.head; while (current != NULL) { struct dir_hash_node *next = current->next; @@ -1150,6 +1290,26 @@ fts_err: goto cleanup; } + +/* + * Public API + */ + +/* selinux_restorecon(3) - Main function that is responsible for labeling */ +int selinux_restorecon(const char *pathname_orig, + unsigned int restorecon_flags) +{ + return selinux_restorecon_common(pathname_orig, restorecon_flags, 1); +} + +/* selinux_restorecon_parallel(3) - Parallel version of selinux_restorecon(3) */ +int selinux_restorecon_parallel(const char *pathname_orig, + unsigned int restorecon_flags, + size_t nthreads) +{ + return selinux_restorecon_common(pathname_orig, restorecon_flags, nthreads); +} + /* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */ void selinux_restorecon_set_sehandle(struct selabel_handle *hndl) { diff --git a/libselinux/src/selinuxswig_python.i b/libselinux/src/selinuxswig_python.i index 4c73bf92..17e03b9e 100644 --- a/libselinux/src/selinuxswig_python.i +++ b/libselinux/src/selinuxswig_python.i @@ -20,7 +20,7 @@ DISABLED = -1 PERMISSIVE = 0 ENFORCING = 1 -def restorecon(path, recursive=False, verbose=False, force=False): +def restorecon(path, recursive=False, verbose=False, force=False, nthreads=1): """ Restore SELinux context on a given path Arguments: @@ -32,6 +32,8 @@ def restorecon(path, recursive=False, verbose=False, force=False): force -- Force reset of context to match file_context for customizable files, and the default file context, changing the user, role, range portion as well as the type (default False) + nthreads -- The number of threads to use during relabeling, or 0 to use as many + threads as there are online CPU cores (default 1) """ restorecon_flags = SELINUX_RESTORECON_IGNORE_DIGEST | SELINUX_RESTORECON_REALPATH @@ -41,7 +43,7 @@ def restorecon(path, recursive=False, verbose=False, force=False): restorecon_flags |= SELINUX_RESTORECON_VERBOSE if force: restorecon_flags |= SELINUX_RESTORECON_SET_SPECFILE_CTX - selinux_restorecon(os.path.expanduser(path), restorecon_flags) + selinux_restorecon_parallel(os.path.expanduser(path), restorecon_flags, nthreads) def chcon(path, context, recursive=False): """ Set the SELinux context on a given path """ diff --git a/libselinux/src/selinuxswig_python_exception.i b/libselinux/src/selinuxswig_python_exception.i index 237ea69a..a02f4923 100644 --- a/libselinux/src/selinuxswig_python_exception.i +++ b/libselinux/src/selinuxswig_python_exception.i @@ -1183,6 +1183,14 @@ } } +%exception selinux_restorecon_parallel { + $action + if (result < 0) { + PyErr_SetFromErrno(PyExc_OSError); + SWIG_fail; + } +} + %exception selinux_restorecon_set_alt_rootpath { $action if (result < 0) { diff --git a/libselinux/src/setrans_client.c b/libselinux/src/setrans_client.c index 52a8ba78..faa12681 100644 --- a/libselinux/src/setrans_client.c +++ b/libselinux/src/setrans_client.c @@ -272,7 +272,7 @@ static inline void init_thread_destructor(void) if (!has_setrans) return; if (destructor_initialized == 0) { - __selinux_setspecific(destructor_key, (void *)1); + __selinux_setspecific(destructor_key, /* some valid address to please GCC */ &selinux_page_size); destructor_initialized = 1; } } diff --git a/libselinux/utils/Makefile b/libselinux/utils/Makefile index 36816155..801066cb 100644 --- a/libselinux/utils/Makefile +++ b/libselinux/utils/Makefile @@ -44,7 +44,6 @@ endif override CFLAGS += -I../include -D_GNU_SOURCE $(DISABLE_FLAGS) $(PCRE_CFLAGS) override LDFLAGS += -L../src override LDLIBS += -lselinux $(FTS_LDLIBS) -PCRE_LDLIBS ?= -lpcre ifeq ($(ANDROID_HOST),y) TARGETS=sefcontext_compile diff --git a/libsemanage/include/semanage/handle.h b/libsemanage/include/semanage/handle.h index 946d69bc..0157be4f 100644 --- a/libsemanage/include/semanage/handle.h +++ b/libsemanage/include/semanage/handle.h @@ -66,6 +66,11 @@ extern void semanage_set_reload(semanage_handle_t * handle, int do_reload); * 1 for yes, 0 for no (default) */ extern void semanage_set_rebuild(semanage_handle_t * handle, int do_rebuild); +/* set whether to rebuild the policy on commit when potential changes + * to module files since last rebuild are detected, + * 1 for yes (default), 0 for no */ +extern void semanage_set_check_ext_changes(semanage_handle_t * handle, int do_check); + /* Fills *compiler_path with the location of the hll compiler sh->conf->compiler_directory_path * corresponding to lang_ext. * Upon success returns 0, -1 on error. */ diff --git a/libsemanage/include/semanage/modules.h b/libsemanage/include/semanage/modules.h index b51f61f0..14666f6d 100644 --- a/libsemanage/include/semanage/modules.h +++ b/libsemanage/include/semanage/modules.h @@ -282,4 +282,30 @@ extern int semanage_module_get_enabled(semanage_handle_t *sh, const semanage_module_key_t *modkey, int *enabled); +/* Compute checksum for @modkey module contents. + * + * If @checksum is NULL, the function will just return the length of the + * checksum string in @checksum_len (checksum strings are guaranteed to + * have a fixed length for a given libsemanage binary). @modkey and @cil + * are ignored in this case and should be set to NULL and 0 (respectively). + * + * If @checksum is non-NULL, on success, @checksum will point to a buffer + * containing the checksum string and @checksum_len will point to the + * length of the string (without the null terminator). The semantics of + * @cil are the same as for @extract_cil in semanage_module_extract(). + * + * The caller is responsible to free the buffer returned in @checksum (using + * free(3)). + * + * Callers may assume that if the checksum strings for two modules match, + * the module content is the same (collisions are theoretically possible, + * yet extremely unlikely). + * + * Returns 0 on success and -1 on error. + */ +extern int semanage_module_compute_checksum(semanage_handle_t *sh, + semanage_module_key_t *modkey, + int cil, char **checksum, + size_t *checksum_len); + #endif diff --git a/libsemanage/man/man5/semanage.conf.5 b/libsemanage/man/man5/semanage.conf.5 index 7d6f2fef..380b58be 100644 --- a/libsemanage/man/man5/semanage.conf.5 +++ b/libsemanage/man/man5/semanage.conf.5 @@ -23,7 +23,7 @@ Management library writes to the SELinux policy module store directly (this is t Otherwise a socket path or a server name can be used for the argument. If the argument begins with "/" (as in "/foo/bar"), it represents the path to a named socket that should be used to connect the policy management server. -If the argument does not begin with a "/" (as in "foo.com:4242"), it should be interpreted as the name of a remote policy management server +If the argument does not begin with a "/" (as in "example.com:4242"), it should be interpreted as the name of a remote policy management server to be used through a TCP connection (default port is 4242 unless a different one is specified after the server name using the colon to separate the two fields). diff --git a/libsemanage/man/ru/man5/semanage.conf.5 b/libsemanage/man/ru/man5/semanage.conf.5 index cf65b3e6..548aa58d 100644 --- a/libsemanage/man/ru/man5/semanage.conf.5 +++ b/libsemanage/man/ru/man5/semanage.conf.5 @@ -19,7 +19,7 @@ semanage.conf \- глобальный файл конфигурации для Указать, как библиотека управления SELinux должна взаимодействовать с хранилищем политики SELinux. Если установлено "direct", библиотека управления SELinux выполняет запись напрямую в хранилище модулей политики SELinux (это значение по умолчанию). В ином случае в качестве аргумента может использоваться путь к сокету или имя сервера. Если аргумент начинается с "/" (как в "/foo/bar"), он представляет собой путь к именованному сокету, который следует использовать для подключения сервера управления политикой. -Если аргумент не начинается с "/" (как в "foo.com:4242"), он должен интерпретироваться как имя удалённого сервера управления политикой, который следует использовать через TCP-подключение (порт по умолчанию 4242, если только после имени сервера через двоеточие, разделяющее два поля, не указан другой порт). +Если аргумент не начинается с "/" (как в "example.com:4242"), он должен интерпретироваться как имя удалённого сервера управления политикой, который следует использовать через TCP-подключение (порт по умолчанию 4242, если только после имени сервера через двоеточие, разделяющее два поля, не указан другой порт). .TP .B root diff --git a/libsemanage/src/boolean_record.c b/libsemanage/src/boolean_record.c index 95f3a862..40dc6545 100644 --- a/libsemanage/src/boolean_record.c +++ b/libsemanage/src/boolean_record.c @@ -7,6 +7,9 @@ */ #include <string.h> +#include <stdio.h> +#include <stdlib.h> + #include <sepol/boolean_record.h> typedef sepol_bool_t semanage_bool_t; @@ -20,7 +23,6 @@ typedef semanage_bool_key_t record_key_t; #include "boolean_internal.h" #include "handle.h" #include "database.h" -#include <stdlib.h> #include <selinux/selinux.h> /* Key */ diff --git a/libsemanage/src/booleans_file.c b/libsemanage/src/booleans_file.c index f79d0b44..6d600bbc 100644 --- a/libsemanage/src/booleans_file.c +++ b/libsemanage/src/booleans_file.c @@ -48,7 +48,7 @@ static int bool_parse(semanage_handle_t * handle, goto last; /* Extract name */ - if (parse_fetch_string(handle, info, &str, '=') < 0) + if (parse_fetch_string(handle, info, &str, '=', 0) < 0) goto err; if (semanage_bool_set_name(handle, boolean, str) < 0) diff --git a/libsemanage/src/compressed_file.c b/libsemanage/src/compressed_file.c new file mode 100644 index 00000000..5546b830 --- /dev/null +++ b/libsemanage/src/compressed_file.c @@ -0,0 +1,224 @@ +/* Author: Jason Tang <jtang@tresys.com> + * Christopher Ashworth <cashworth@tresys.com> + * Ondrej Mosnacek <omosnacek@gmail.com> + * + * Copyright (C) 2004-2006 Tresys Technology, LLC + * Copyright (C) 2005-2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +#include <unistd.h> +#include <fcntl.h> + +#include <bzlib.h> + +#include "compressed_file.h" + +#include "debug.h" + +#define BZ2_MAGICSTR "BZh" +#define BZ2_MAGICLEN (sizeof(BZ2_MAGICSTR)-1) + +/* bzip() a data to a file, returning the total number of compressed bytes + * in the file. Returns -1 if file could not be compressed. */ +static int bzip(semanage_handle_t *sh, const char *filename, void *data, + size_t num_bytes) +{ + BZFILE* b; + size_t size = 1<<16; + int bzerror; + size_t total = 0; + size_t len = 0; + FILE *f; + + if ((f = fopen(filename, "wb")) == NULL) { + return -1; + } + + if (!sh->conf->bzip_blocksize) { + if (fwrite(data, 1, num_bytes, f) < num_bytes) { + fclose(f); + return -1; + } + fclose(f); + return 0; + } + + b = BZ2_bzWriteOpen( &bzerror, f, sh->conf->bzip_blocksize, 0, 0); + if (bzerror != BZ_OK) { + BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 ); + fclose(f); + return -1; + } + + while ( num_bytes > total ) { + if (num_bytes - total > size) { + len = size; + } else { + len = num_bytes - total; + } + BZ2_bzWrite ( &bzerror, b, (uint8_t *)data + total, len ); + if (bzerror == BZ_IO_ERROR) { + BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 ); + fclose(f); + return -1; + } + total += len; + } + + BZ2_bzWriteClose ( &bzerror, b, 0, 0, 0 ); + fclose(f); + if (bzerror == BZ_IO_ERROR) { + return -1; + } + return 0; +} + +/* bunzip() a file to '*data', returning the total number of uncompressed bytes + * in the file. Returns -1 if file could not be decompressed. */ +static ssize_t bunzip(semanage_handle_t *sh, FILE *f, void **data) +{ + BZFILE* b = NULL; + size_t nBuf; + uint8_t* buf = NULL; + size_t size = 1<<18; + size_t bufsize = size; + int bzerror; + size_t total = 0; + uint8_t* uncompress = NULL; + uint8_t* tmpalloc = NULL; + int ret = -1; + + buf = malloc(bufsize); + if (buf == NULL) { + ERR(sh, "Failure allocating memory."); + goto exit; + } + + /* Check if the file is bzipped */ + bzerror = fread(buf, 1, BZ2_MAGICLEN, f); + rewind(f); + if ((bzerror != BZ2_MAGICLEN) || memcmp(buf, BZ2_MAGICSTR, BZ2_MAGICLEN)) { + goto exit; + } + + b = BZ2_bzReadOpen ( &bzerror, f, 0, sh->conf->bzip_small, NULL, 0 ); + if ( bzerror != BZ_OK ) { + ERR(sh, "Failure opening bz2 archive."); + goto exit; + } + + uncompress = malloc(size); + if (uncompress == NULL) { + ERR(sh, "Failure allocating memory."); + goto exit; + } + + while ( bzerror == BZ_OK) { + nBuf = BZ2_bzRead ( &bzerror, b, buf, bufsize); + if (( bzerror == BZ_OK ) || ( bzerror == BZ_STREAM_END )) { + if (total + nBuf > size) { + size *= 2; + tmpalloc = realloc(uncompress, size); + if (tmpalloc == NULL) { + ERR(sh, "Failure allocating memory."); + goto exit; + } + uncompress = tmpalloc; + } + memcpy(&uncompress[total], buf, nBuf); + total += nBuf; + } + } + if ( bzerror != BZ_STREAM_END ) { + ERR(sh, "Failure reading bz2 archive."); + goto exit; + } + + ret = total; + *data = uncompress; + +exit: + BZ2_bzReadClose ( &bzerror, b ); + free(buf); + if ( ret < 0 ) { + free(uncompress); + } + return ret; +} + +int map_compressed_file(semanage_handle_t *sh, const char *path, + struct file_contents *contents) +{ + ssize_t size = -1; + void *uncompress; + int ret = 0, fd = -1; + FILE *file = NULL; + + fd = open(path, O_RDONLY); + if (fd == -1) { + ERR(sh, "Unable to open %s\n", path); + return -1; + } + + file = fdopen(fd, "r"); + if (file == NULL) { + ERR(sh, "Unable to open %s\n", path); + close(fd); + return -1; + } + + if ((size = bunzip(sh, file, &uncompress)) >= 0) { + contents->data = uncompress; + contents->len = size; + contents->compressed = 1; + } else { + struct stat sb; + if (fstat(fd, &sb) == -1 || + (uncompress = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == + MAP_FAILED) { + ret = -1; + } else { + contents->data = uncompress; + contents->len = sb.st_size; + contents->compressed = 0; + } + } + fclose(file); + return ret; +} + +void unmap_compressed_file(struct file_contents *contents) +{ + if (!contents->data) + return; + + if (contents->compressed) { + free(contents->data); + } else { + munmap(contents->data, contents->len); + } +} + +int write_compressed_file(semanage_handle_t *sh, const char *path, + void *data, size_t len) +{ + return bzip(sh, path, data, len); +} diff --git a/libsemanage/src/compressed_file.h b/libsemanage/src/compressed_file.h new file mode 100644 index 00000000..96cfb4b6 --- /dev/null +++ b/libsemanage/src/compressed_file.h @@ -0,0 +1,78 @@ +/* Author: Jason Tang <jtang@tresys.com> + * Christopher Ashworth <cashworth@tresys.com> + * Ondrej Mosnacek <omosnacek@gmail.com> + * + * Copyright (C) 2004-2006 Tresys Technology, LLC + * Copyright (C) 2005-2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _SEMANAGE_CIL_FILE_H_ +#define _SEMANAGE_CIL_FILE_H_ + +#include <sys/mman.h> +#include <sys/types.h> + +#include "handle.h" + +struct file_contents { + void *data; /** file contents (uncompressed) */ + size_t len; /** length of contents */ + int compressed; /** whether file was compressed */ +}; + +/** + * Map/read a possibly-compressed file into memory. + * + * If the file is bzip compressed map_file will uncompress the file into + * @p contents. The caller is responsible for calling + * @ref unmap_compressed_file on @p contents on success. + * + * @param sh semanage handle + * @param path path to the file + * @param contents pointer to struct file_contents, which will be + * populated with data pointer, size, and an indication whether + * the file was compressed or not + * + * @return 0 on success, -1 otherwise. + */ +int map_compressed_file(semanage_handle_t *sh, const char *path, + struct file_contents *contents); + +/** + * Destroy a previously mapped possibly-compressed file. + * + * If all fields of @p contents are zero/NULL, the function is + * guaranteed to do nothing. + * + * @param contents pointer to struct file_contents to destroy + */ +void unmap_compressed_file(struct file_contents *contents); + +/** + * Write bytes into a file, using compression if configured. + * + * @param sh semanage handle + * @param path path to the file + * @param data pointer to the data + * @param len length of the data + * + * @return 0 on success, -1 otherwise. + */ +int write_compressed_file(semanage_handle_t *sh, const char *path, + void *data, size_t len); + +#endif diff --git a/libsemanage/src/context_record.c b/libsemanage/src/context_record.c index 16ba518e..dafb90fc 100644 --- a/libsemanage/src/context_record.c +++ b/libsemanage/src/context_record.c @@ -7,6 +7,8 @@ typedef sepol_context_t semanage_context_t; #define _SEMANAGE_CONTEXT_DEFINED_ +#include <semanage/context_record.h> + /* User */ const char *semanage_context_get_user(const semanage_context_t * con) { diff --git a/libsemanage/src/database_join.c b/libsemanage/src/database_join.c index b9b35a61..a49a6226 100644 --- a/libsemanage/src/database_join.c +++ b/libsemanage/src/database_join.c @@ -77,10 +77,14 @@ static int dbase_join_cache(semanage_handle_t * handle, dbase_join_t * dbase) goto err; /* Sort for quicker merge later */ - qsort(records1, rcount1, sizeof(record1_t *), - (int (*)(const void *, const void *))rtable1->compare2_qsort); - qsort(records2, rcount2, sizeof(record2_t *), - (int (*)(const void *, const void *))rtable2->compare2_qsort); + if (rcount1 > 0) { + qsort(records1, rcount1, sizeof(record1_t *), + (int (*)(const void *, const void *))rtable1->compare2_qsort); + } + if (rcount2 > 0) { + qsort(records2, rcount2, sizeof(record2_t *), + (int (*)(const void *, const void *))rtable2->compare2_qsort); + } /* Now merge into this dbase */ while (i < rcount1 || j < rcount2) { diff --git a/libsemanage/src/direct_api.c b/libsemanage/src/direct_api.c index f0e2300a..d83941b0 100644 --- a/libsemanage/src/direct_api.c +++ b/libsemanage/src/direct_api.c @@ -33,6 +33,8 @@ #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> +#include <sys/mman.h> +#include <sys/wait.h> #include <limits.h> #include <errno.h> #include <dirent.h> @@ -50,13 +52,13 @@ #include "debug.h" #include "handle.h" +#include "compressed_file.h" #include "modules.h" #include "direct_api.h" #include "semanage_store.h" #include "database_policydb.h" #include "policy.h" -#include <sys/mman.h> -#include <sys/wait.h> +#include "sha256.h" #define PIPE_READ 0 #define PIPE_WRITE 1 @@ -446,198 +448,10 @@ static int parse_module_headers(semanage_handle_t * sh, char *module_data, return 0; } -#include <stdlib.h> -#include <bzlib.h> -#include <string.h> -#include <sys/sendfile.h> - -/* bzip() a data to a file, returning the total number of compressed bytes - * in the file. Returns -1 if file could not be compressed. */ -static ssize_t bzip(semanage_handle_t *sh, const char *filename, char *data, - size_t num_bytes) -{ - BZFILE* b; - size_t size = 1<<16; - int bzerror; - size_t total = 0; - size_t len = 0; - FILE *f; - - if ((f = fopen(filename, "wb")) == NULL) { - return -1; - } - - if (!sh->conf->bzip_blocksize) { - if (fwrite(data, 1, num_bytes, f) < num_bytes) { - fclose(f); - return -1; - } - fclose(f); - return num_bytes; - } - - b = BZ2_bzWriteOpen( &bzerror, f, sh->conf->bzip_blocksize, 0, 0); - if (bzerror != BZ_OK) { - BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 ); - return -1; - } - - while ( num_bytes > total ) { - if (num_bytes - total > size) { - len = size; - } else { - len = num_bytes - total; - } - BZ2_bzWrite ( &bzerror, b, &data[total], len ); - if (bzerror == BZ_IO_ERROR) { - BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 ); - return -1; - } - total += len; - } - - BZ2_bzWriteClose ( &bzerror, b, 0, 0, 0 ); - fclose(f); - if (bzerror == BZ_IO_ERROR) { - return -1; - } - return total; -} - -#define BZ2_MAGICSTR "BZh" -#define BZ2_MAGICLEN (sizeof(BZ2_MAGICSTR)-1) - -/* bunzip() a file to '*data', returning the total number of uncompressed bytes - * in the file. Returns -1 if file could not be decompressed. */ -ssize_t bunzip(semanage_handle_t *sh, FILE *f, char **data) -{ - BZFILE* b = NULL; - size_t nBuf; - char* buf = NULL; - size_t size = 1<<18; - size_t bufsize = size; - int bzerror; - size_t total = 0; - char* uncompress = NULL; - char* tmpalloc = NULL; - int ret = -1; - - buf = malloc(bufsize); - if (buf == NULL) { - ERR(sh, "Failure allocating memory."); - goto exit; - } - - /* Check if the file is bzipped */ - bzerror = fread(buf, 1, BZ2_MAGICLEN, f); - rewind(f); - if ((bzerror != BZ2_MAGICLEN) || memcmp(buf, BZ2_MAGICSTR, BZ2_MAGICLEN)) { - goto exit; - } - - b = BZ2_bzReadOpen ( &bzerror, f, 0, sh->conf->bzip_small, NULL, 0 ); - if ( bzerror != BZ_OK ) { - ERR(sh, "Failure opening bz2 archive."); - goto exit; - } - - uncompress = malloc(size); - if (uncompress == NULL) { - ERR(sh, "Failure allocating memory."); - goto exit; - } - - while ( bzerror == BZ_OK) { - nBuf = BZ2_bzRead ( &bzerror, b, buf, bufsize); - if (( bzerror == BZ_OK ) || ( bzerror == BZ_STREAM_END )) { - if (total + nBuf > size) { - size *= 2; - tmpalloc = realloc(uncompress, size); - if (tmpalloc == NULL) { - ERR(sh, "Failure allocating memory."); - goto exit; - } - uncompress = tmpalloc; - } - memcpy(&uncompress[total], buf, nBuf); - total += nBuf; - } - } - if ( bzerror != BZ_STREAM_END ) { - ERR(sh, "Failure reading bz2 archive."); - goto exit; - } - - ret = total; - *data = uncompress; - -exit: - BZ2_bzReadClose ( &bzerror, b ); - free(buf); - if ( ret < 0 ) { - free(uncompress); - } - return ret; -} - -/* mmap() a file to '*data', - * If the file is bzip compressed map_file will uncompress - * the file into '*data'. - * Returns the total number of bytes in memory . - * Returns -1 if file could not be opened or mapped. */ -static ssize_t map_file(semanage_handle_t *sh, const char *path, char **data, - int *compressed) -{ - ssize_t size = -1; - char *uncompress; - int fd = -1; - FILE *file = NULL; - - fd = open(path, O_RDONLY); - if (fd == -1) { - ERR(sh, "Unable to open %s\n", path); - return -1; - } - - file = fdopen(fd, "r"); - if (file == NULL) { - ERR(sh, "Unable to open %s\n", path); - close(fd); - return -1; - } - - if ((size = bunzip(sh, file, &uncompress)) > 0) { - *data = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); - if (*data == MAP_FAILED) { - free(uncompress); - fclose(file); - return -1; - } else { - memcpy(*data, uncompress, size); - } - free(uncompress); - *compressed = 1; - } else { - struct stat sb; - if (fstat(fd, &sb) == -1 || - (*data = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == - MAP_FAILED) { - size = -1; - } else { - size = sb.st_size; - } - *compressed = 0; - } - - fclose(file); - - return size; -} - /* Writes a block of data to a file. Returns 0 on success, -1 on * error. */ static int write_file(semanage_handle_t * sh, - const char *filename, char *data, size_t num_bytes) + const char *filename, const char *data, size_t num_bytes) { int out; @@ -1037,23 +851,33 @@ cleanup: return ret; } +static void update_checksum_with_len(Sha256Context *context, size_t s) +{ + int i; + uint8_t buffer[8]; + + for (i = 0; i < 8; i++) { + buffer[i] = s & 0xff; + s >>= 8; + } + Sha256Update(context, buffer, 8); +} + static int semanage_compile_module(semanage_handle_t *sh, - semanage_module_info_t *modinfo) + semanage_module_info_t *modinfo, + Sha256Context *context) { char cil_path[PATH_MAX]; char hll_path[PATH_MAX]; char *compiler_path = NULL; char *cil_data = NULL; char *err_data = NULL; - char *hll_data = NULL; char *start = NULL; char *end = NULL; - ssize_t hll_data_len = 0; - ssize_t bzip_status; int status = 0; - int compressed; size_t cil_data_len = 0; size_t err_data_len = 0; + struct file_contents hll_contents = {}; if (!strcasecmp(modinfo->lang_ext, "cil")) { goto cleanup; @@ -1084,13 +908,15 @@ static int semanage_compile_module(semanage_handle_t *sh, goto cleanup; } - if ((hll_data_len = map_file(sh, hll_path, &hll_data, &compressed)) <= 0) { + status = map_compressed_file(sh, hll_path, &hll_contents); + if (status < 0) { ERR(sh, "Unable to read file %s\n", hll_path); - status = -1; goto cleanup; } - status = semanage_pipe_data(sh, compiler_path, hll_data, (size_t)hll_data_len, &cil_data, &cil_data_len, &err_data, &err_data_len); + status = semanage_pipe_data(sh, compiler_path, hll_contents.data, + hll_contents.len, &cil_data, &cil_data_len, + &err_data, &err_data_len); if (err_data_len > 0) { for (start = end = err_data; end < err_data + err_data_len; end++) { if (*end == '\n') { @@ -1110,10 +936,14 @@ static int semanage_compile_module(semanage_handle_t *sh, goto cleanup; } - bzip_status = bzip(sh, cil_path, cil_data, cil_data_len); - if (bzip_status == -1) { - ERR(sh, "Failed to bzip %s\n", cil_path); - status = -1; + if (context) { + update_checksum_with_len(context, cil_data_len); + Sha256Update(context, cil_data, cil_data_len); + } + + status = write_compressed_file(sh, cil_path, cil_data, cil_data_len); + if (status == -1) { + ERR(sh, "Failed to write %s\n", cil_path); goto cleanup; } @@ -1131,9 +961,7 @@ static int semanage_compile_module(semanage_handle_t *sh, } cleanup: - if (hll_data_len > 0) { - munmap(hll_data, hll_data_len); - } + unmap_compressed_file(&hll_contents); free(cil_data); free(err_data); free(compiler_path); @@ -1141,18 +969,40 @@ cleanup: return status; } +static int modinfo_cmp(const void *a, const void *b) +{ + const semanage_module_info_t *ma = a; + const semanage_module_info_t *mb = b; + + return strcmp(ma->name, mb->name); +} + static int semanage_compile_hll_modules(semanage_handle_t *sh, - semanage_module_info_t *modinfos, - int num_modinfos) + semanage_module_info_t *modinfos, + int num_modinfos, + char *cil_checksum) { - int status = 0; - int i; + /* to be incremented when checksum input data format changes */ + static const size_t CHECKSUM_EPOCH = 1; + + int i, status = 0; char cil_path[PATH_MAX]; struct stat sb; + Sha256Context context; + SHA256_HASH hash; + struct file_contents contents = {}; assert(sh); assert(modinfos); + /* Sort modules by name to get consistent ordering. */ + qsort(modinfos, num_modinfos, sizeof(*modinfos), &modinfo_cmp); + + Sha256Initialise(&context); + update_checksum_with_len(&context, CHECKSUM_EPOCH); + + /* prefix with module count to avoid collisions */ + update_checksum_with_len(&context, num_modinfos); for (i = 0; i < num_modinfos; i++) { status = semanage_module_get_path( sh, @@ -1160,31 +1010,103 @@ static int semanage_compile_hll_modules(semanage_handle_t *sh, SEMANAGE_MODULE_PATH_CIL, cil_path, sizeof(cil_path)); - if (status != 0) { - goto cleanup; - } + if (status != 0) + return -1; - if (semanage_get_ignore_module_cache(sh) == 0 && - (status = stat(cil_path, &sb)) == 0) { - continue; - } - if (status != 0 && errno != ENOENT) { - ERR(sh, "Unable to access %s: %s\n", cil_path, strerror(errno)); - goto cleanup; //an error in the "stat" call + if (!semanage_get_ignore_module_cache(sh)) { + status = stat(cil_path, &sb); + if (status == 0) { + status = map_compressed_file(sh, cil_path, &contents); + if (status < 0) { + ERR(sh, "Error mapping file: %s", cil_path); + return -1; + } + + /* prefix with length to avoid collisions */ + update_checksum_with_len(&context, contents.len); + Sha256Update(&context, contents.data, contents.len); + + unmap_compressed_file(&contents); + continue; + } else if (errno != ENOENT) { + ERR(sh, "Unable to access %s: %s\n", cil_path, + strerror(errno)); + return -1; //an error in the "stat" call + } } - status = semanage_compile_module(sh, &modinfos[i]); - if (status < 0) { - goto cleanup; + status = semanage_compile_module(sh, &modinfos[i], &context); + if (status < 0) + return -1; + } + Sha256Finalise(&context, &hash); + + semanage_hash_to_checksum_string(hash.bytes, cil_checksum); + return 0; +} + +static int semanage_compare_checksum(semanage_handle_t *sh, const char *reference) +{ + const char *path = semanage_path(SEMANAGE_TMP, SEMANAGE_MODULES_CHECKSUM); + struct stat sb; + int fd, retval; + char *data; + + fd = open(path, O_RDONLY); + if (fd == -1) { + if (errno != ENOENT) { + ERR(sh, "Unable to open %s: %s\n", path, strerror(errno)); + return -1; } + /* Checksum file not present - force a rebuild. */ + return 1; } - status = 0; + if (fstat(fd, &sb) == -1) { + ERR(sh, "Unable to stat %s\n", path); + retval = -1; + goto out_close; + } -cleanup: - return status; + if (sb.st_size != (off_t)CHECKSUM_CONTENT_SIZE) { + /* Incompatible/invalid hash type - just force a rebuild. */ + WARN(sh, "Module checksum invalid - forcing a rebuild\n"); + retval = 1; + goto out_close; + } + + data = mmap(NULL, CHECKSUM_CONTENT_SIZE, PROT_READ, MAP_PRIVATE, fd, 0); + if (data == MAP_FAILED) { + ERR(sh, "Unable to mmap %s\n", path); + retval = -1; + goto out_close; + } + + retval = memcmp(data, reference, CHECKSUM_CONTENT_SIZE) != 0; + munmap(data, sb.st_size); +out_close: + close(fd); + return retval; +} + +static int semanage_write_modules_checksum(semanage_handle_t *sh, + const char *checksum) +{ + const char *path = semanage_path(SEMANAGE_TMP, SEMANAGE_MODULES_CHECKSUM); + + return write_file(sh, path, checksum, CHECKSUM_CONTENT_SIZE); } +/* Files that must exist in order to skip policy rebuild. */ +static const int semanage_computed_files[] = { + SEMANAGE_STORE_KERNEL, + SEMANAGE_STORE_FC, + SEMANAGE_STORE_SEUSERS, + SEMANAGE_LINKED, + SEMANAGE_SEUSERS_LINKED, + SEMANAGE_USERS_EXTRA_LINKED +}; + /* Copies a file from src to dst. If dst already exists then * overwrite it. If source doesn't exist then return success. * Returns 0 on success, -1 on error. */ @@ -1211,6 +1133,7 @@ static int semanage_direct_commit(semanage_handle_t * sh) semanage_module_info_t *modinfos = NULL; mode_t mask = umask(0077); struct stat sb; + char modules_checksum[CHECKSUM_CONTENT_SIZE + 1 /* '\0' */]; int do_rebuild, do_write_kernel, do_install; int fcontexts_modified, ports_modified, seusers_modified, @@ -1244,6 +1167,14 @@ static int semanage_direct_commit(semanage_handle_t * sh) seusers_modified = seusers->dtable->is_modified(seusers->dbase); fcontexts_modified = fcontexts->dtable->is_modified(fcontexts->dbase); + /* Before we do anything else, flush the join to its component parts. + * This *does not* flush to disk automatically */ + if (users->dtable->is_modified(users->dbase)) { + retval = users->dtable->flush(sh, users->dbase); + if (retval < 0) + goto cleanup; + } + /* Rebuild if explicitly requested or any module changes occurred. */ do_rebuild = sh->do_rebuild | sh->modules_modified; @@ -1310,14 +1241,6 @@ static int semanage_direct_commit(semanage_handle_t * sh) } } - /* Before we do anything else, flush the join to its component parts. - * This *does not* flush to disk automatically */ - if (users->dtable->is_modified(users->dbase)) { - retval = users->dtable->flush(sh, users->dbase); - if (retval < 0) - goto cleanup; - } - /* * This is for systems that have already migrated with an older version * of semanage_migrate_store. The older version did not copy @@ -1326,70 +1249,59 @@ static int semanage_direct_commit(semanage_handle_t * sh) * in order to skip re-linking are present; otherwise, we force * a rebuild. */ - if (!do_rebuild) { - int files[] = {SEMANAGE_STORE_KERNEL, - SEMANAGE_STORE_FC, - SEMANAGE_STORE_SEUSERS, - SEMANAGE_LINKED, - SEMANAGE_SEUSERS_LINKED, - SEMANAGE_USERS_EXTRA_LINKED}; - - for (i = 0; i < (int) ARRAY_SIZE(files); i++) { - path = semanage_path(SEMANAGE_TMP, files[i]); - if (stat(path, &sb) != 0) { - if (errno != ENOENT) { - ERR(sh, "Unable to access %s: %s\n", path, strerror(errno)); - retval = -1; - goto cleanup; - } - - do_rebuild = 1; - goto rebuild; + for (i = 0; !do_rebuild && i < (int)ARRAY_SIZE(semanage_computed_files); i++) { + path = semanage_path(SEMANAGE_TMP, semanage_computed_files[i]); + if (stat(path, &sb) != 0) { + if (errno != ENOENT) { + ERR(sh, "Unable to access %s: %s\n", path, strerror(errno)); + retval = -1; + goto cleanup; } + + do_rebuild = 1; + break; } } -rebuild: - /* - * Now that we know whether or not a rebuild is required, - * we can determine what else needs to be done. - * We need to write the kernel policy if we are rebuilding - * or if any other policy component that lives in the kernel - * policy has been modified. - * We need to install the policy files if any of the managed files - * that live under /etc/selinux (kernel policy, seusers, file contexts) - * will be modified. - */ - do_write_kernel = do_rebuild | ports_modified | ibpkeys_modified | - ibendports_modified | - bools->dtable->is_modified(bools->dbase) | - ifaces->dtable->is_modified(ifaces->dbase) | - nodes->dtable->is_modified(nodes->dbase) | - users->dtable->is_modified(users_base->dbase); - do_install = do_write_kernel | seusers_modified | fcontexts_modified; - - /* - * If there were policy changes, or explicitly requested, or - * any required files are missing, rebuild the policy. - */ - if (do_rebuild) { - /* =================== Module expansion =============== */ - + if (do_rebuild || sh->check_ext_changes) { retval = semanage_get_active_modules(sh, &modinfos, &num_modinfos); if (retval < 0) { goto cleanup; } + /* No modules - nothing to rebuild. */ if (num_modinfos == 0) { goto cleanup; } - retval = semanage_compile_hll_modules(sh, modinfos, num_modinfos); + retval = semanage_compile_hll_modules(sh, modinfos, num_modinfos, + modules_checksum); if (retval < 0) { ERR(sh, "Failed to compile hll files into cil files.\n"); goto cleanup; } + if (!do_rebuild && sh->check_ext_changes) { + retval = semanage_compare_checksum(sh, modules_checksum); + if (retval < 0) + goto cleanup; + do_rebuild = retval; + } + + retval = semanage_write_modules_checksum(sh, modules_checksum); + if (retval < 0) { + ERR(sh, "Failed to write module checksum file.\n"); + goto cleanup; + } + } + + /* + * If there were policy changes, or explicitly requested, or + * any required files are missing, rebuild the policy. + */ + if (do_rebuild) { + /* =================== Module expansion =============== */ + retval = semanage_get_cil_paths(sh, modinfos, num_modinfos, &mod_filenames); if (retval < 0) goto cleanup; @@ -1521,6 +1433,23 @@ rebuild: } } + /* + * Determine what else needs to be done. + * We need to write the kernel policy if we are rebuilding + * or if any other policy component that lives in the kernel + * policy has been modified. + * We need to install the policy files if any of the managed files + * that live under /etc/selinux (kernel policy, seusers, file contexts) + * will be modified. + */ + do_write_kernel = do_rebuild | ports_modified | ibpkeys_modified | + ibendports_modified | + bools->dtable->is_modified(bools->dbase) | + ifaces->dtable->is_modified(ifaces->dbase) | + nodes->dtable->is_modified(nodes->dbase) | + users->dtable->is_modified(users_base->dbase); + do_install = do_write_kernel | seusers_modified | fcontexts_modified; + /* Attach our databases to the policydb we just created or loaded. */ dbase_policydb_attach((dbase_policydb_t *) pusers_base->dbase, out); dbase_policydb_attach((dbase_policydb_t *) pports->dbase, out); @@ -1756,19 +1685,17 @@ static int semanage_direct_install_file(semanage_handle_t * sh, { int retval = -1; - char *data = NULL; - ssize_t data_len = 0; - int compressed = 0; char *path = NULL; char *filename; char *lang_ext = NULL; char *module_name = NULL; char *separator; char *version = NULL; + struct file_contents contents = {}; - if ((data_len = map_file(sh, install_filename, &data, &compressed)) <= 0) { + retval = map_compressed_file(sh, install_filename, &contents); + if (retval < 0) { ERR(sh, "Unable to read file %s\n", install_filename); - retval = -1; goto cleanup; } @@ -1781,7 +1708,7 @@ static int semanage_direct_install_file(semanage_handle_t * sh, filename = basename(path); - if (compressed) { + if (contents.compressed) { separator = strrchr(filename, '.'); if (separator == NULL) { ERR(sh, "Compressed module does not have a valid extension."); @@ -1805,7 +1732,8 @@ static int semanage_direct_install_file(semanage_handle_t * sh, } if (strcmp(lang_ext, "pp") == 0) { - retval = parse_module_headers(sh, data, data_len, &module_name, &version); + retval = parse_module_headers(sh, contents.data, contents.len, + &module_name, &version); free(version); if (retval != 0) goto cleanup; @@ -1822,10 +1750,11 @@ static int semanage_direct_install_file(semanage_handle_t * sh, fprintf(stderr, "Warning: SELinux userspace will refer to the module from %s as %s rather than %s\n", install_filename, module_name, filename); } - retval = semanage_direct_install(sh, data, data_len, module_name, lang_ext); + retval = semanage_direct_install(sh, contents.data, contents.len, + module_name, lang_ext); cleanup: - if (data_len > 0) munmap(data, data_len); + unmap_compressed_file(&contents); free(module_name); free(path); @@ -1844,10 +1773,8 @@ static int semanage_direct_extract(semanage_handle_t * sh, enum semanage_module_path_type file_type; int rc = -1; semanage_module_info_t *_modinfo = NULL; - ssize_t _data_len; - char *_data; - int compressed; struct stat sb; + struct file_contents contents = {}; /* get path of module */ rc = semanage_module_get_path( @@ -1897,25 +1824,39 @@ static int semanage_direct_extract(semanage_handle_t * sh, goto cleanup; } - rc = semanage_compile_module(sh, _modinfo); + rc = semanage_compile_module(sh, _modinfo, NULL); if (rc < 0) { goto cleanup; } } - _data_len = map_file(sh, input_file, &_data, &compressed); - if (_data_len <= 0) { + rc = map_compressed_file(sh, input_file, &contents); + if (rc < 0) { ERR(sh, "Error mapping file: %s", input_file); - rc = -1; goto cleanup; } + /* The API promises an mmap'ed pointer */ + if (contents.compressed) { + *mapped_data = mmap(NULL, contents.len, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); + if (*mapped_data == MAP_FAILED) { + ERR(sh, "Unable to map memory"); + rc = -1; + goto cleanup; + } + memcpy(*mapped_data, contents.data, contents.len); + free(contents.data); + } else { + *mapped_data = contents.data; + } + *modinfo = _modinfo; - *data_len = (size_t)_data_len; - *mapped_data = _data; + *data_len = contents.len; cleanup: if (rc != 0) { + unmap_compressed_file(&contents); semanage_module_info_destroy(sh, _modinfo); free(_modinfo); } @@ -2869,8 +2810,8 @@ static int semanage_direct_install_info(semanage_handle_t *sh, goto cleanup; } - ret = bzip(sh, path, data, data_len); - if (ret <= 0) { + ret = write_compressed_file(sh, path, data, data_len); + if (ret < 0) { ERR(sh, "Error while writing to %s.", path); status = -3; goto cleanup; diff --git a/libsemanage/src/direct_api.h b/libsemanage/src/direct_api.h index e56107b2..ffd428eb 100644 --- a/libsemanage/src/direct_api.h +++ b/libsemanage/src/direct_api.h @@ -39,8 +39,4 @@ int semanage_direct_access_check(struct semanage_handle *sh); int semanage_direct_mls_enabled(struct semanage_handle *sh); -#include <stdio.h> -#include <unistd.h> -ssize_t bunzip(struct semanage_handle *sh, FILE *f, char **data); - #endif diff --git a/libsemanage/src/fcontexts_file.c b/libsemanage/src/fcontexts_file.c index 04cd365a..f3579410 100644 --- a/libsemanage/src/fcontexts_file.c +++ b/libsemanage/src/fcontexts_file.c @@ -90,7 +90,7 @@ static int fcontext_parse(semanage_handle_t * handle, goto last; /* Regexp */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_fcontext_set_expr(handle, fcontext, str) < 0) goto err; @@ -100,7 +100,7 @@ static int fcontext_parse(semanage_handle_t * handle, /* Type */ if (parse_assert_space(handle, info) < 0) goto err; - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (!strcasecmp(str, "-s")) semanage_fcontext_set_type(fcontext, SEMANAGE_FCONTEXT_SOCK); @@ -124,7 +124,7 @@ static int fcontext_parse(semanage_handle_t * handle, /* Context */ if (parse_assert_space(handle, info) < 0) goto err; - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; process_context: diff --git a/libsemanage/src/handle.c b/libsemanage/src/handle.c index bb1e6140..b2201ee3 100644 --- a/libsemanage/src/handle.c +++ b/libsemanage/src/handle.c @@ -116,20 +116,23 @@ semanage_handle_t *semanage_handle_create(void) void semanage_set_rebuild(semanage_handle_t * sh, int do_rebuild) { - assert(sh != NULL); sh->do_rebuild = do_rebuild; - return; } void semanage_set_reload(semanage_handle_t * sh, int do_reload) { - assert(sh != NULL); sh->do_reload = do_reload; - return; +} + +void semanage_set_check_ext_changes(semanage_handle_t * sh, int do_check) +{ + assert(sh != NULL); + + sh->check_ext_changes = do_check; } int semanage_get_hll_compiler_path(semanage_handle_t *sh, diff --git a/libsemanage/src/handle.h b/libsemanage/src/handle.h index e1ce83ba..4d2aae8f 100644 --- a/libsemanage/src/handle.h +++ b/libsemanage/src/handle.h @@ -61,6 +61,7 @@ struct semanage_handle { int is_in_transaction; int do_reload; /* whether to reload policy after commit */ int do_rebuild; /* whether to rebuild policy if there were no changes */ + int check_ext_changes; /* whether to rebuild if external changes are detected via checksum */ int commit_err; /* set by semanage_direct_commit() if there are * any errors when building or committing the * sandbox to kernel policy at /etc/selinux diff --git a/libsemanage/src/ibendports_file.c b/libsemanage/src/ibendports_file.c index bafa8c1d..2fa2a67c 100644 --- a/libsemanage/src/ibendports_file.c +++ b/libsemanage/src/ibendports_file.c @@ -75,7 +75,7 @@ static int ibendport_parse(semanage_handle_t *handle, goto err; /* IB Device Name */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_ibendport_set_ibdev_name(handle, ibendport, str) < 0) goto err; @@ -92,7 +92,7 @@ static int ibendport_parse(semanage_handle_t *handle, /* context */ if (parse_assert_space(handle, info) < 0) goto err; - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_context_from_string(handle, str, &con) < 0) { ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s", diff --git a/libsemanage/src/ibpkeys_file.c b/libsemanage/src/ibpkeys_file.c index 929bc31e..edde69f0 100644 --- a/libsemanage/src/ibpkeys_file.c +++ b/libsemanage/src/ibpkeys_file.c @@ -80,7 +80,7 @@ static int ibpkey_parse(semanage_handle_t *handle, goto err; /* Subnet Prefix */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_ibpkey_set_subnet_prefix(handle, ibpkey, str) < 0) goto err; @@ -115,7 +115,7 @@ static int ibpkey_parse(semanage_handle_t *handle, semanage_ibpkey_set_pkey(ibpkey, low); } /* Pkey context */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_context_from_string(handle, str, &con) < 0) { ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s", diff --git a/libsemanage/src/interfaces_file.c b/libsemanage/src/interfaces_file.c index c19c8f94..244f0ae5 100644 --- a/libsemanage/src/interfaces_file.c +++ b/libsemanage/src/interfaces_file.c @@ -72,7 +72,7 @@ static int iface_parse(semanage_handle_t * handle, goto err; /* Name */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_iface_set_name(handle, iface, str) < 0) goto err; @@ -82,7 +82,7 @@ static int iface_parse(semanage_handle_t * handle, /* Interface context */ if (parse_assert_space(handle, info) < 0) goto err; - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_context_from_string(handle, str, &con) < 0) { ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s", @@ -106,7 +106,7 @@ static int iface_parse(semanage_handle_t * handle, /* Message context */ if (parse_assert_space(handle, info) < 0) goto err; - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_context_from_string(handle, str, &con) < 0) { ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s", diff --git a/libsemanage/src/libsemanage.map b/libsemanage/src/libsemanage.map index 3ea7b60f..c8214b26 100644 --- a/libsemanage/src/libsemanage.map +++ b/libsemanage/src/libsemanage.map @@ -345,3 +345,8 @@ LIBSEMANAGE_1.1 { semanage_module_remove_key; semanage_set_store_root; } LIBSEMANAGE_1.0; + +LIBSEMANAGE_3.4 { + semanage_module_compute_checksum; + semanage_set_check_ext_changes; +} LIBSEMANAGE_1.1; diff --git a/libsemanage/src/modules.c b/libsemanage/src/modules.c index b6dd456c..c3bd90ac 100644 --- a/libsemanage/src/modules.c +++ b/libsemanage/src/modules.c @@ -35,11 +35,13 @@ #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/mman.h> #include <errno.h> #include <ctype.h> #include "handle.h" #include "modules.h" +#include "sha256.h" #include "debug.h" int semanage_module_install(semanage_handle_t * sh, @@ -168,6 +170,7 @@ const char *semanage_module_get_name(semanage_module_info_t * modinfo) /* Legacy function that remains to preserve ABI * compatibility. */ +extern const char *semanage_module_get_version(semanage_module_info_t *); const char *semanage_module_get_version(semanage_module_info_t * modinfo __attribute__ ((unused))) { @@ -975,3 +978,60 @@ int semanage_module_remove_key(semanage_handle_t *sh, return sh->funcs->remove_key(sh, modkey); } +static const char CHECKSUM_TYPE[] = "sha256"; +const size_t CHECKSUM_CONTENT_SIZE = sizeof(CHECKSUM_TYPE) + 1 + 2 * SHA256_HASH_SIZE; + +void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum) +{ + size_t i; + + checksum += sprintf(checksum, "%s:", CHECKSUM_TYPE); + for (i = 0; i < SHA256_HASH_SIZE; i++) { + checksum += sprintf(checksum, "%02x", (unsigned)hash[i]); + } +} + +int semanage_module_compute_checksum(semanage_handle_t *sh, + semanage_module_key_t *modkey, + int cil, char **checksum, + size_t *checksum_len) +{ + semanage_module_info_t *extract_info = NULL; + Sha256Context context; + SHA256_HASH sha256_hash; + char *checksum_str; + void *data; + size_t data_len = 0; + int result; + + if (!checksum_len) + return -1; + + if (!checksum) { + *checksum_len = CHECKSUM_CONTENT_SIZE; + return 0; + } + + result = semanage_module_extract(sh, modkey, cil, &data, &data_len, &extract_info); + if (result != 0) + return -1; + + semanage_module_info_destroy(sh, extract_info); + free(extract_info); + + Sha256Initialise(&context); + Sha256Update(&context, data, data_len); + Sha256Finalise(&context, &sha256_hash); + + munmap(data, data_len); + + checksum_str = malloc(CHECKSUM_CONTENT_SIZE + 1 /* '\0' */); + if (!checksum_str) + return -1; + + semanage_hash_to_checksum_string(sha256_hash.bytes, checksum_str); + + *checksum = checksum_str; + *checksum_len = CHECKSUM_CONTENT_SIZE; + return 0; +} diff --git a/libsemanage/src/modules.h b/libsemanage/src/modules.h index 64d4a157..cf2432c5 100644 --- a/libsemanage/src/modules.h +++ b/libsemanage/src/modules.h @@ -102,4 +102,7 @@ int semanage_module_get_path(semanage_handle_t *sh, char *path, size_t len); +extern const size_t CHECKSUM_CONTENT_SIZE; +void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum); + #endif diff --git a/libsemanage/src/nodes_file.c b/libsemanage/src/nodes_file.c index c3647f2a..2d2b7fe0 100644 --- a/libsemanage/src/nodes_file.c +++ b/libsemanage/src/nodes_file.c @@ -77,7 +77,7 @@ static int node_parse(semanage_handle_t * handle, goto err; /* Protocol */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (!strcasecmp(str, "ipv4")) proto = SEMANAGE_PROTO_IP4; @@ -96,7 +96,7 @@ static int node_parse(semanage_handle_t * handle, /* Address */ if (parse_assert_space(handle, info) < 0) goto err; - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_node_set_addr(handle, node, proto, str) < 0) goto err; @@ -106,7 +106,7 @@ static int node_parse(semanage_handle_t * handle, str = NULL; /* Netmask */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_node_set_mask(handle, node, proto, str) < 0) goto err; @@ -116,7 +116,7 @@ static int node_parse(semanage_handle_t * handle, str = NULL; /* Port context */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_context_from_string(handle, str, &con) < 0) { ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s", diff --git a/libsemanage/src/parse_utils.c b/libsemanage/src/parse_utils.c index 4fb54fc3..918dee43 100644 --- a/libsemanage/src/parse_utils.c +++ b/libsemanage/src/parse_utils.c @@ -239,7 +239,7 @@ int parse_fetch_int(semanage_handle_t * handle, char *test = NULL; int value = 0; - if (parse_fetch_string(handle, info, &str, delim) < 0) + if (parse_fetch_string(handle, info, &str, delim, 0) < 0) goto err; if (!isdigit((int)*str)) { @@ -267,7 +267,7 @@ int parse_fetch_int(semanage_handle_t * handle, } int parse_fetch_string(semanage_handle_t * handle, - parse_info_t * info, char **str, char delim) + parse_info_t * info, char **str, char delim, int allow_spaces) { char *start = info->ptr; @@ -277,7 +277,7 @@ int parse_fetch_string(semanage_handle_t * handle, if (parse_assert_noeof(handle, info) < 0) goto err; - while (*(info->ptr) && !isspace(*(info->ptr)) && + while (*(info->ptr) && (allow_spaces || !isspace(*(info->ptr))) && (*(info->ptr) != delim)) { info->ptr++; len++; diff --git a/libsemanage/src/parse_utils.h b/libsemanage/src/parse_utils.h index 0f334860..3e44aca1 100644 --- a/libsemanage/src/parse_utils.h +++ b/libsemanage/src/parse_utils.h @@ -71,12 +71,11 @@ extern int parse_optional_str(parse_info_t * info, const char *str); int parse_fetch_int(semanage_handle_t * hgandle, parse_info_t * info, int *num, char delim); -/* Extract the next string (delimited by - * whitespace), and move the read pointer past it. - * Stop of the optional character delim is encountered, - * or if whitespace/eof is encountered. Fail if the - * string is of length 0. */ +/* Extract the next string and move the read pointer past it. + * Stop if the optional character delim (or eof) is encountered, + * or if whitespace is encountered and allow_spaces is 0. + * Fail if the string is of length 0. */ extern int parse_fetch_string(semanage_handle_t * handle, - parse_info_t * info, char **str_ptr, char delim); + parse_info_t * info, char **str_ptr, char delim, int allow_spaces); #endif diff --git a/libsemanage/src/ports_file.c b/libsemanage/src/ports_file.c index ade4102f..1356021a 100644 --- a/libsemanage/src/ports_file.c +++ b/libsemanage/src/ports_file.c @@ -77,7 +77,7 @@ static int port_parse(semanage_handle_t * handle, goto err; /* Protocol */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (!strcasecmp(str, "tcp")) semanage_port_set_proto(port, SEMANAGE_PROTO_TCP); @@ -123,7 +123,7 @@ static int port_parse(semanage_handle_t * handle, semanage_port_set_port(port, low); /* Port context */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_context_from_string(handle, str, &con) < 0) { ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s", diff --git a/libsemanage/src/semanage.conf b/libsemanage/src/semanage.conf index dc8d46b8..98d769b5 100644 --- a/libsemanage/src/semanage.conf +++ b/libsemanage/src/semanage.conf @@ -24,8 +24,9 @@ # /foo/bar - Write by way of a policy management server, whose # named socket is at /foo/bar. The path must begin # with a '/'. -# foo.com:4242 - Establish a TCP connection to a remote policy -# management server at foo.com. If there is a colon +# example.com:4242 +# - Establish a TCP connection to a remote policy +# management server at example.com. If there is a colon # then the remainder is interpreted as a port number; # otherwise default to port 4242. module-store = direct diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c index c6a736fe..767f05cb 100644 --- a/libsemanage/src/semanage_store.c +++ b/libsemanage/src/semanage_store.c @@ -59,6 +59,7 @@ typedef struct dbase_policydb dbase_t; #include "debug.h" #include "utilities.h" +#include "compressed_file.h" #define SEMANAGE_CONF_FILE "semanage.conf" /* relative path names to enum semanage_paths to special files and @@ -114,6 +115,7 @@ static const char *semanage_sandbox_paths[SEMANAGE_STORE_NUM_PATHS] = { "/disable_dontaudit", "/preserve_tunables", "/modules/disabled", + "/modules_checksum", "/policy.kern", "/file_contexts.local", "/file_contexts.homedirs", @@ -2054,60 +2056,27 @@ int semanage_direct_get_serial(semanage_handle_t * sh) int semanage_load_files(semanage_handle_t * sh, cil_db_t *cildb, char **filenames, int numfiles) { - int retval = 0; - FILE *fp; - ssize_t size; - char *data = NULL; + int i, retval = 0; char *filename; - int i; + struct file_contents contents = {}; for (i = 0; i < numfiles; i++) { filename = filenames[i]; - if ((fp = fopen(filename, "rb")) == NULL) { - ERR(sh, "Could not open module file %s for reading.", filename); - goto cleanup; - } - - if ((size = bunzip(sh, fp, &data)) <= 0) { - rewind(fp); - __fsetlocking(fp, FSETLOCKING_BYCALLER); - - if (fseek(fp, 0, SEEK_END) != 0) { - ERR(sh, "Failed to determine size of file %s.", filename); - goto cleanup; - } - size = ftell(fp); - rewind(fp); - - data = malloc(size); - if (fread(data, size, 1, fp) != 1) { - ERR(sh, "Failed to read file %s.", filename); - goto cleanup; - } - } + retval = map_compressed_file(sh, filename, &contents); + if (retval < 0) + return -1; - fclose(fp); - fp = NULL; + retval = cil_add_file(cildb, filename, contents.data, contents.len); + unmap_compressed_file(&contents); - retval = cil_add_file(cildb, filename, data, size); if (retval != SEPOL_OK) { ERR(sh, "Error while reading from file %s.", filename); - goto cleanup; + return -1; } - - free(data); - data = NULL; } - return retval; - - cleanup: - if (fp != NULL) { - fclose(fp); - } - free(data); - return -1; + return 0; } /* diff --git a/libsemanage/src/semanage_store.h b/libsemanage/src/semanage_store.h index b9ec5664..1fc77da8 100644 --- a/libsemanage/src/semanage_store.h +++ b/libsemanage/src/semanage_store.h @@ -60,6 +60,7 @@ enum semanage_sandbox_defs { SEMANAGE_DISABLE_DONTAUDIT, SEMANAGE_PRESERVE_TUNABLES, SEMANAGE_MODULES_DISABLED, + SEMANAGE_MODULES_CHECKSUM, SEMANAGE_STORE_KERNEL, SEMANAGE_STORE_FC_LOCAL, SEMANAGE_STORE_FC_HOMEDIRS, diff --git a/libsemanage/src/semanageswig_python_exception.i b/libsemanage/src/semanageswig_python_exception.i index 372ec948..0df8bbc3 100644 --- a/libsemanage/src/semanageswig_python_exception.i +++ b/libsemanage/src/semanageswig_python_exception.i @@ -351,6 +351,14 @@ } } +%exception semanage_module_compute_checksum { + $action + if (result < 0) { + PyErr_SetFromErrno(PyExc_OSError); + SWIG_fail; + } +} + %exception semanage_msg_get_level { $action if (result < 0) { diff --git a/libsemanage/src/seusers_file.c b/libsemanage/src/seusers_file.c index 910bedf4..21b970ac 100644 --- a/libsemanage/src/seusers_file.c +++ b/libsemanage/src/seusers_file.c @@ -53,7 +53,7 @@ static int seuser_parse(semanage_handle_t * handle, goto last; /* Extract name */ - if (parse_fetch_string(handle, info, &str, ':') < 0) + if (parse_fetch_string(handle, info, &str, ':', 1) < 0) goto err; if (semanage_seuser_set_name(handle, seuser, str) < 0) goto err; @@ -68,7 +68,7 @@ static int seuser_parse(semanage_handle_t * handle, goto err; /* Extract sename */ - if (parse_fetch_string(handle, info, &str, ':') < 0) + if (parse_fetch_string(handle, info, &str, ':', 1) < 0) goto err; if (semanage_seuser_set_sename(handle, seuser, str) < 0) goto err; @@ -83,7 +83,7 @@ static int seuser_parse(semanage_handle_t * handle, goto err; /* NOTE: does not allow spaces/multiline */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_seuser_set_mlsrange(handle, seuser, str) < 0) diff --git a/libsemanage/src/sha256.c b/libsemanage/src/sha256.c new file mode 100644 index 00000000..fe2aeef0 --- /dev/null +++ b/libsemanage/src/sha256.c @@ -0,0 +1,294 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// WjCryptLib_Sha256 +// +// Implementation of SHA256 hash function. +// Original author: Tom St Denis, tomstdenis@gmail.com, http://libtom.org +// Modified by WaterJuice retaining Public Domain license. +// +// This is free and unencumbered software released into the public domain - June 2013 waterjuice.org +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// IMPORTS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "sha256.h" +#include <memory.h> + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// MACROS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits)))) + +#define MIN(x, y) ( ((x)<(y))?(x):(y) ) + +#define STORE32H(x, y) \ + { (y)[0] = (uint8_t)(((x)>>24)&255); (y)[1] = (uint8_t)(((x)>>16)&255); \ + (y)[2] = (uint8_t)(((x)>>8)&255); (y)[3] = (uint8_t)((x)&255); } + +#define LOAD32H(x, y) \ + { x = ((uint32_t)((y)[0] & 255)<<24) | \ + ((uint32_t)((y)[1] & 255)<<16) | \ + ((uint32_t)((y)[2] & 255)<<8) | \ + ((uint32_t)((y)[3] & 255)); } + +#define STORE64H(x, y) \ + { (y)[0] = (uint8_t)(((x)>>56)&255); (y)[1] = (uint8_t)(((x)>>48)&255); \ + (y)[2] = (uint8_t)(((x)>>40)&255); (y)[3] = (uint8_t)(((x)>>32)&255); \ + (y)[4] = (uint8_t)(((x)>>24)&255); (y)[5] = (uint8_t)(((x)>>16)&255); \ + (y)[6] = (uint8_t)(((x)>>8)&255); (y)[7] = (uint8_t)((x)&255); } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CONSTANTS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// The K array +static const uint32_t K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +#define BLOCK_SIZE 64 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// INTERNAL FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Various logical functions +#define Ch( x, y, z ) (z ^ (x & (y ^ z))) +#define Maj( x, y, z ) (((x | y) & z) | (x & y)) +#define S( x, n ) ror((x),(n)) +#define R( x, n ) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0( x ) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1( x ) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0( x ) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1( x ) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +#define Sha256Round( a, b, c, d, e, f, g, h, i ) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TransformFunction +// +// Compress 512-bits +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static +void + TransformFunction + ( + Sha256Context* Context, + uint8_t const* Buffer + ) +{ + uint32_t S[8]; + uint32_t W[64]; + uint32_t t0; + uint32_t t1; + uint32_t t; + int i; + + // Copy state into S + for( i=0; i<8; i++ ) + { + S[i] = Context->state[i]; + } + + // Copy the state into 512-bits into W[0..15] + for( i=0; i<16; i++ ) + { + LOAD32H( W[i], Buffer + (4*i) ); + } + + // Fill W[16..63] + for( i=16; i<64; i++ ) + { + W[i] = Gamma1( W[i-2]) + W[i-7] + Gamma0( W[i-15] ) + W[i-16]; + } + + // Compress + for( i=0; i<64; i++ ) + { + Sha256Round( S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i ); + t = S[7]; + S[7] = S[6]; + S[6] = S[5]; + S[5] = S[4]; + S[4] = S[3]; + S[3] = S[2]; + S[2] = S[1]; + S[1] = S[0]; + S[0] = t; + } + + // Feedback + for( i=0; i<8; i++ ) + { + Context->state[i] = Context->state[i] + S[i]; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Initialise +// +// Initialises a SHA256 Context. Use this to initialise/reset a context. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void + Sha256Initialise + ( + Sha256Context* Context // [out] + ) +{ + Context->curlen = 0; + Context->length = 0; + Context->state[0] = 0x6A09E667UL; + Context->state[1] = 0xBB67AE85UL; + Context->state[2] = 0x3C6EF372UL; + Context->state[3] = 0xA54FF53AUL; + Context->state[4] = 0x510E527FUL; + Context->state[5] = 0x9B05688CUL; + Context->state[6] = 0x1F83D9ABUL; + Context->state[7] = 0x5BE0CD19UL; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Update +// +// Adds data to the SHA256 context. This will process the data and update the internal state of the context. Keep on +// calling this function until all the data has been added. Then call Sha256Finalise to calculate the hash. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void + Sha256Update + ( + Sha256Context* Context, // [in out] + void const* Buffer, // [in] + uint32_t BufferSize // [in] + ) +{ + uint32_t n; + + if( Context->curlen > sizeof(Context->buf) ) + { + return; + } + + while( BufferSize > 0 ) + { + if( Context->curlen == 0 && BufferSize >= BLOCK_SIZE ) + { + TransformFunction( Context, (uint8_t*)Buffer ); + Context->length += BLOCK_SIZE * 8; + Buffer = (uint8_t*)Buffer + BLOCK_SIZE; + BufferSize -= BLOCK_SIZE; + } + else + { + n = MIN( BufferSize, (BLOCK_SIZE - Context->curlen) ); + memcpy( Context->buf + Context->curlen, Buffer, (size_t)n ); + Context->curlen += n; + Buffer = (uint8_t*)Buffer + n; + BufferSize -= n; + if( Context->curlen == BLOCK_SIZE ) + { + TransformFunction( Context, Context->buf ); + Context->length += 8*BLOCK_SIZE; + Context->curlen = 0; + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Finalise +// +// Performs the final calculation of the hash and returns the digest (32 byte buffer containing 256bit hash). After +// calling this, Sha256Initialised must be used to reuse the context. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void + Sha256Finalise + ( + Sha256Context* Context, // [in out] + SHA256_HASH* Digest // [out] + ) +{ + int i; + + if( Context->curlen >= sizeof(Context->buf) ) + { + return; + } + + // Increase the length of the message + Context->length += Context->curlen * 8; + + // Append the '1' bit + Context->buf[Context->curlen++] = (uint8_t)0x80; + + // if the length is currently above 56 bytes we append zeros + // then compress. Then we can fall back to padding zeros and length + // encoding like normal. + if( Context->curlen > 56 ) + { + while( Context->curlen < 64 ) + { + Context->buf[Context->curlen++] = (uint8_t)0; + } + TransformFunction(Context, Context->buf); + Context->curlen = 0; + } + + // Pad up to 56 bytes of zeroes + while( Context->curlen < 56 ) + { + Context->buf[Context->curlen++] = (uint8_t)0; + } + + // Store length + STORE64H( Context->length, Context->buf+56 ); + TransformFunction( Context, Context->buf ); + + // Copy output + for( i=0; i<8; i++ ) + { + STORE32H( Context->state[i], Digest->bytes+(4*i) ); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Calculate +// +// Combines Sha256Initialise, Sha256Update, and Sha256Finalise into one function. Calculates the SHA256 hash of the +// buffer. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void + Sha256Calculate + ( + void const* Buffer, // [in] + uint32_t BufferSize, // [in] + SHA256_HASH* Digest // [in] + ) +{ + Sha256Context context; + + Sha256Initialise( &context ); + Sha256Update( &context, Buffer, BufferSize ); + Sha256Finalise( &context, Digest ); +} diff --git a/libsemanage/src/sha256.h b/libsemanage/src/sha256.h new file mode 100644 index 00000000..406ed869 --- /dev/null +++ b/libsemanage/src/sha256.h @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// WjCryptLib_Sha256 +// +// Implementation of SHA256 hash function. +// Original author: Tom St Denis, tomstdenis@gmail.com, http://libtom.org +// Modified by WaterJuice retaining Public Domain license. +// +// This is free and unencumbered software released into the public domain - June 2013 waterjuice.org +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// IMPORTS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include <stdint.h> +#include <stdio.h> + +typedef struct +{ + uint64_t length; + uint32_t state[8]; + uint32_t curlen; + uint8_t buf[64]; +} Sha256Context; + +#define SHA256_HASH_SIZE ( 256 / 8 ) + +typedef struct +{ + uint8_t bytes [SHA256_HASH_SIZE]; +} SHA256_HASH; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Initialise +// +// Initialises a SHA256 Context. Use this to initialise/reset a context. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void + Sha256Initialise + ( + Sha256Context* Context // [out] + ); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Update +// +// Adds data to the SHA256 context. This will process the data and update the internal state of the context. Keep on +// calling this function until all the data has been added. Then call Sha256Finalise to calculate the hash. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void + Sha256Update + ( + Sha256Context* Context, // [in out] + void const* Buffer, // [in] + uint32_t BufferSize // [in] + ); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Finalise +// +// Performs the final calculation of the hash and returns the digest (32 byte buffer containing 256bit hash). After +// calling this, Sha256Initialised must be used to reuse the context. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void + Sha256Finalise + ( + Sha256Context* Context, // [in out] + SHA256_HASH* Digest // [out] + ); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Calculate +// +// Combines Sha256Initialise, Sha256Update, and Sha256Finalise into one function. Calculates the SHA256 hash of the +// buffer. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void + Sha256Calculate + ( + void const* Buffer, // [in] + uint32_t BufferSize, // [in] + SHA256_HASH* Digest // [in] + ); diff --git a/libsemanage/src/users_base_file.c b/libsemanage/src/users_base_file.c index 0f0a8fdb..a0f8cd7e 100644 --- a/libsemanage/src/users_base_file.c +++ b/libsemanage/src/users_base_file.c @@ -83,7 +83,7 @@ static int user_base_parse(semanage_handle_t * handle, goto err; /* Parse user name */ - if (parse_fetch_string(handle, info, &name_str, ' ') < 0) + if (parse_fetch_string(handle, info, &name_str, ' ', 0) < 0) goto err; if (semanage_user_base_set_name(handle, user, name_str) < 0) { @@ -150,7 +150,7 @@ static int user_base_parse(semanage_handle_t * handle, goto err; /* NOTE: does not allow spaces/multiline */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_user_base_set_mlslevel(handle, user, str) < 0) goto err; @@ -165,8 +165,7 @@ static int user_base_parse(semanage_handle_t * handle, if (parse_assert_space(handle, info) < 0) goto err; - /* NOTE: does not allow spaces/multiline */ - if (parse_fetch_string(handle, info, &str, ';') < 0) + if (parse_fetch_string(handle, info, &str, ';', 1) < 0) goto err; if (semanage_user_base_set_mlsrange(handle, user, str) < 0) goto err; diff --git a/libsemanage/src/users_extra_file.c b/libsemanage/src/users_extra_file.c index 8f2bebd6..7aa9df3c 100644 --- a/libsemanage/src/users_extra_file.c +++ b/libsemanage/src/users_extra_file.c @@ -57,7 +57,7 @@ static int user_extra_parse(semanage_handle_t * handle, goto err; /* Extract name */ - if (parse_fetch_string(handle, info, &str, ' ') < 0) + if (parse_fetch_string(handle, info, &str, ' ', 0) < 0) goto err; if (semanage_user_extra_set_name(handle, user_extra, str) < 0) goto err; @@ -73,7 +73,7 @@ static int user_extra_parse(semanage_handle_t * handle, goto err; /* Extract prefix */ - if (parse_fetch_string(handle, info, &str, ';') < 0) + if (parse_fetch_string(handle, info, &str, ';', 1) < 0) goto err; if (semanage_user_extra_set_prefix(handle, user_extra, str) < 0) goto err; diff --git a/libsemanage/src/utilities.c b/libsemanage/src/utilities.c index fc5a6a51..fdbb8ad6 100644 --- a/libsemanage/src/utilities.c +++ b/libsemanage/src/utilities.c @@ -292,7 +292,7 @@ char *semanage_str_replace(const char *search, const char *replace, * * returns the newly created node or NULL on error */ -semanage_list_t *list_addafter_controlmem(semanage_list_t * item, char *data) +static semanage_list_t *list_addafter_controlmem(semanage_list_t * item, char *data) { semanage_list_t *temp = malloc(sizeof(semanage_list_t)); diff --git a/libsemanage/tests/test_bool.c b/libsemanage/tests/test_bool.c index ae80d448..7bf5225b 100644 --- a/libsemanage/tests/test_bool.c +++ b/libsemanage/tests/test_bool.c @@ -132,6 +132,8 @@ semanage_bool_t *get_bool_nth(int idx) if (i != (unsigned int) idx) semanage_bool_free(records[i]); + free(records); + return boolean; } @@ -163,6 +165,8 @@ semanage_bool_key_t *get_bool_key_nth(int idx) CU_ASSERT_FATAL(res >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(key); + semanage_bool_free(boolean); + return key; } @@ -196,6 +200,9 @@ void add_local_bool(const char *name) CU_ASSERT_PTR_NOT_NULL_FATAL(boolean); CU_ASSERT_FATAL(semanage_bool_modify_local(sh, key, boolean) >= 0); + + semanage_bool_key_free(key); + semanage_bool_free(boolean); } void delete_local_bool(const char *name) @@ -208,6 +215,8 @@ void delete_local_bool(const char *name) CU_ASSERT_PTR_NOT_NULL_FATAL(key); CU_ASSERT_FATAL(semanage_bool_del_local(sh, key) >= 0); + + semanage_bool_key_free(key); } /* Function bool_key_create */ @@ -447,6 +456,8 @@ void helper_bool_create(level_t level) CU_ASSERT_PTR_NULL(semanage_bool_get_name(boolean)); CU_ASSERT(semanage_bool_get_value(boolean) == 0); + semanage_bool_free(boolean); + cleanup_handle(level); } @@ -483,6 +494,9 @@ void helper_bool_clone(level_t level, int bool_idx) CU_ASSERT_EQUAL(val, val_clone); + semanage_bool_free(boolean_clone); + semanage_bool_free(boolean); + cleanup_handle(level); } @@ -514,6 +528,9 @@ void helper_bool_query(level_t level, const char *bool_str, int exp_res) CU_ASSERT_PTR_NULL(resp); } + semanage_bool_free(resp); + semanage_bool_key_free(key); + cleanup_handle(level); } @@ -647,6 +664,8 @@ void helper_bool_list(level_t level) for (unsigned int i = 0; i < count; i++) semanage_bool_free(records[i]); + free(records); + cleanup_handle(level); } @@ -662,7 +681,7 @@ void helper_bool_modify_del_local(level_t level, const char *name, int old_val, int exp_res) { semanage_bool_t *boolean; - semanage_bool_t *boolean_local; + semanage_bool_t *boolean_local = NULL; semanage_bool_key_t *key = NULL; int res; int new_val; @@ -696,6 +715,8 @@ void helper_bool_modify_del_local(level_t level, const char *name, CU_ASSERT(semanage_bool_query_local(sh, key, &boolean_local) >= 0); CU_ASSERT(semanage_bool_compare2(boolean_local, boolean) == 0); + semanage_bool_free(boolean_local); + CU_ASSERT(semanage_bool_del_local(sh, key) >= 0); CU_ASSERT(semanage_bool_query_local(sh, key, &boolean_local) < 0); @@ -734,15 +755,18 @@ void test_bool_query_local(void) /* transaction */ setup_handle(SH_TRANS); + semanage_bool_key_free(key); CU_ASSERT(semanage_bool_key_create(sh, BOOL1_NAME, &key) >= 0); CU_ASSERT_PTR_NOT_NULL(key); CU_ASSERT(semanage_bool_query_local(sh, key, &resp) < 0); CU_ASSERT_PTR_NULL(resp); + semanage_bool_free(resp); add_local_bool(BOOL1_NAME); CU_ASSERT(semanage_bool_query_local(sh, key, &resp) >= 0); CU_ASSERT_PTR_NOT_NULL(resp); + semanage_bool_free(resp); semanage_bool_key_free(key); CU_ASSERT(semanage_bool_key_create(sh, BOOL2_NAME, &key) >= 0); @@ -751,8 +775,10 @@ void test_bool_query_local(void) add_local_bool(BOOL2_NAME); CU_ASSERT(semanage_bool_query_local(sh, key, &resp) >= 0); CU_ASSERT_PTR_NOT_NULL(resp); + semanage_bool_free(resp); /* cleanup */ + semanage_bool_key_free(key); delete_local_bool(BOOL1_NAME); delete_local_bool(BOOL2_NAME); cleanup_handle(SH_TRANS); @@ -784,6 +810,7 @@ void test_bool_exists_local(void) CU_ASSERT(resp == 0); /* cleanup */ + semanage_bool_key_free(key); cleanup_handle(SH_TRANS); } @@ -918,12 +945,17 @@ void test_bool_list_local(void) CU_ASSERT(semanage_bool_list_local(sh, &records, &count) >= 0); CU_ASSERT(count == init_count + 1); CU_ASSERT_PTR_NOT_NULL(records[0]); + semanage_bool_free(records[0]); + free(records); add_local_bool(BOOL2_NAME); CU_ASSERT(semanage_bool_list_local(sh, &records, &count) >= 0); CU_ASSERT(count == init_count + 2); CU_ASSERT_PTR_NOT_NULL(records[0]); CU_ASSERT_PTR_NOT_NULL(records[1]); + semanage_bool_free(records[0]); + semanage_bool_free(records[1]); + free(records); /* cleanup */ delete_local_bool(BOOL1_NAME); diff --git a/libsemanage/tests/test_fcontext.c b/libsemanage/tests/test_fcontext.c index 62af711f..a5fcf849 100644 --- a/libsemanage/tests/test_fcontext.c +++ b/libsemanage/tests/test_fcontext.c @@ -214,6 +214,8 @@ semanage_fcontext_t *get_fcontext_nth(int idx) if (i != (unsigned int) idx) semanage_fcontext_free(records[i]); + free(records); + return fcontext; } @@ -230,6 +232,8 @@ semanage_fcontext_key_t *get_fcontext_key_nth(int idx) CU_ASSERT_FATAL(semanage_fcontext_key_extract(sh, fcontext, &key) >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(key); + semanage_fcontext_free(fcontext); + return key; } @@ -246,6 +250,10 @@ void add_local_fcontext(int fcontext_idx) CU_ASSERT_PTR_NOT_NULL_FATAL(key); CU_ASSERT_FATAL(semanage_fcontext_modify_local(sh, key, fcontext) >= 0); + + /* cleanup */ + semanage_fcontext_key_free(key); + semanage_fcontext_free(fcontext); } void delete_local_fcontext(int fcontext_idx) @@ -257,6 +265,8 @@ void delete_local_fcontext(int fcontext_idx) key = get_fcontext_key_nth(fcontext_idx); CU_ASSERT_FATAL(semanage_fcontext_del_local(sh, key) >= 0); + + semanage_fcontext_key_free(key); } semanage_fcontext_key_t *get_fcontext_key_from_str(const char *str, int type) @@ -477,6 +487,7 @@ void helper_fcontext_get_set_con(level_t level, int fcontext_idx, } /* cleanup */ + semanage_context_free(con); semanage_fcontext_free(fcontext); cleanup_handle(level); } @@ -587,12 +598,14 @@ void helper_fcontext_query(level_t level, const char *fcontext_expr, CU_ASSERT(res >= 0); const char *expr = semanage_fcontext_get_expr(resp); CU_ASSERT_STRING_EQUAL(expr, fcontext_expr); + semanage_fcontext_free(resp); } else { CU_ASSERT(res < 0); CU_ASSERT(resp == (void *) 42); } /* cleanup */ + semanage_fcontext_key_free(key); cleanup_handle(level); } @@ -752,6 +765,8 @@ void helper_fcontext_list(level_t level) for (unsigned int i = 0; i < count; i++) semanage_fcontext_free(records[i]); + free(records); + /* cleanup */ cleanup_handle(level); } @@ -768,7 +783,7 @@ void helper_fcontext_modify_del_local(level_t level, int fcontext_idx, const char *con_str, int exp_res) { semanage_fcontext_t *fcontext; - semanage_fcontext_t *fcontext_local; + semanage_fcontext_t *fcontext_local = NULL; semanage_fcontext_key_t *key = NULL; semanage_context_t *con = NULL; int res; @@ -803,6 +818,8 @@ void helper_fcontext_modify_del_local(level_t level, int fcontext_idx, &fcontext_local) >= 0); CU_ASSERT(semanage_fcontext_compare2(fcontext_local, fcontext) == 0); + semanage_fcontext_free(fcontext_local); + CU_ASSERT(semanage_fcontext_del_local(sh, key) >= 0); CU_ASSERT(semanage_fcontext_query_local(sh, key, &fcontext_local) < 0); @@ -811,6 +828,7 @@ void helper_fcontext_modify_del_local(level_t level, int fcontext_idx, } /* cleanup */ + semanage_context_free(con); semanage_fcontext_key_free(key); semanage_fcontext_free(fcontext); cleanup_handle(level); @@ -846,6 +864,7 @@ void test_fcontext_query_local(void) /* transaction */ setup_handle(SH_TRANS); + semanage_fcontext_key_free(key); key = get_fcontext_key_nth(I_FIRST); CU_ASSERT(semanage_fcontext_query_local(sh, key, &resp) < 0); CU_ASSERT_PTR_NULL(resp); @@ -853,14 +872,19 @@ void test_fcontext_query_local(void) add_local_fcontext(I_FIRST); CU_ASSERT(semanage_fcontext_query_local(sh, key, &resp) >= 0); CU_ASSERT_PTR_NOT_NULL(resp); + semanage_fcontext_free(resp); + resp = NULL; semanage_fcontext_key_free(key); key = get_fcontext_key_nth(I_SECOND); add_local_fcontext(I_SECOND); CU_ASSERT(semanage_fcontext_query_local(sh, key, &resp) >= 0); CU_ASSERT_PTR_NOT_NULL(resp); + semanage_fcontext_free(resp); + resp = NULL; /* cleanup */ + semanage_fcontext_key_free(key); delete_local_fcontext(I_FIRST); delete_local_fcontext(I_SECOND); cleanup_handle(SH_TRANS); @@ -898,6 +922,7 @@ void test_fcontext_exists_local(void) CU_ASSERT(resp == 0); /* cleanup */ + semanage_fcontext_key_free(key); cleanup_handle(SH_TRANS); } @@ -1031,12 +1056,17 @@ void test_fcontext_list_local(void) CU_ASSERT(semanage_fcontext_list_local(sh, &records, &count) >= 0); CU_ASSERT(count == 1); CU_ASSERT_PTR_NOT_NULL(records[0]); + semanage_fcontext_free(records[0]); + free(records); add_local_fcontext(I_SECOND); CU_ASSERT(semanage_fcontext_list_local(sh, &records, &count) >= 0); CU_ASSERT(count == 2); CU_ASSERT_PTR_NOT_NULL(records[0]); CU_ASSERT_PTR_NOT_NULL(records[1]); + semanage_fcontext_free(records[0]); + semanage_fcontext_free(records[1]); + free(records); /* cleanup */ delete_local_fcontext(I_FIRST); diff --git a/libsemanage/tests/test_ibendport.c b/libsemanage/tests/test_ibendport.c index 79a8e2c8..8addc908 100644 --- a/libsemanage/tests/test_ibendport.c +++ b/libsemanage/tests/test_ibendport.c @@ -113,6 +113,8 @@ semanage_ibendport_t *get_ibendport_nth(int idx) if (i != (unsigned int) idx) semanage_ibendport_free(records[i]); + free(records); + return ibendport; } @@ -132,6 +134,8 @@ semanage_ibendport_key_t *get_ibendport_key_nth(int idx) CU_ASSERT_FATAL(res >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(key); + semanage_ibendport_free(ibendport); + return key; } @@ -148,6 +152,9 @@ void add_local_ibendport(int idx) CU_ASSERT_FATAL(semanage_ibendport_modify_local(sh, key, ibendport) >= 0); + + semanage_ibendport_key_free(key); + semanage_ibendport_free(ibendport); } void delete_local_ibendport(int idx) @@ -155,6 +162,8 @@ void delete_local_ibendport(int idx) semanage_ibendport_key_t *key = NULL; key = get_ibendport_key_nth(idx); CU_ASSERT_FATAL(semanage_ibendport_del_local(sh, key) >= 0); + + semanage_ibendport_key_free(key); } /* Function semanage_ibendport_query */ @@ -195,7 +204,9 @@ void test_ibendport_query(void) CU_ASSERT_CONTEXT_EQUAL(con, con_exp); /* cleanup */ + free(name_exp); free(name); + semanage_ibendport_key_free(key); semanage_ibendport_free(ibendport); semanage_ibendport_free(ibendport_exp); cleanup_handle(SH_CONNECT); @@ -356,12 +367,14 @@ void test_ibendport_modify_del_query_local(void) CU_ASSERT(semanage_ibendport_query_local(sh, key, &ibendport_local) >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(ibendport_local); + semanage_ibendport_free(ibendport_local); CU_ASSERT(semanage_ibendport_del_local(sh, key) >= 0); CU_ASSERT(semanage_ibendport_query_local(sh, key, &ibendport_local) < 0); /* cleanup */ + semanage_ibendport_key_free(key); semanage_ibendport_free(ibendport); cleanup_handle(SH_TRANS); } diff --git a/libsemanage/tests/test_iface.c b/libsemanage/tests/test_iface.c index d5d530a8..434372f8 100644 --- a/libsemanage/tests/test_iface.c +++ b/libsemanage/tests/test_iface.c @@ -139,6 +139,8 @@ semanage_iface_t *get_iface_nth(int idx) if (i != (unsigned int) idx) semanage_iface_free(records[i]); + free(records); + return iface; } @@ -157,6 +159,9 @@ semanage_iface_key_t *get_iface_key_nth(int idx) CU_ASSERT_FATAL(res >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(key); + /* cleanup */ + semanage_iface_free(iface); + return key; } @@ -171,6 +176,10 @@ void add_local_iface(int idx) CU_ASSERT_PTR_NOT_NULL_FATAL(key); CU_ASSERT_FATAL(semanage_iface_modify_local(sh, key, iface) >= 0); + + /* cleanup */ + semanage_iface_key_free(key); + semanage_iface_free(iface); } void delete_local_iface(int idx) @@ -178,6 +187,9 @@ void delete_local_iface(int idx) semanage_iface_key_t *key = NULL; key = get_iface_key_nth(idx); CU_ASSERT_FATAL(semanage_iface_del_local(sh, key) >= 0); + + /* cleanup */ + semanage_iface_key_free(key); } /* Function semanage_iface_compare */ @@ -309,6 +321,7 @@ void test_iface_get_set_ifcon(void) CU_ASSERT_CONTEXT_EQUAL(con1, con2); /* cleanup */ + semanage_context_free(con1); semanage_iface_free(iface); cleanup_handle(SH_CONNECT); } @@ -332,6 +345,7 @@ void test_iface_get_set_msgcon(void) CU_ASSERT_CONTEXT_EQUAL(con1, con2); /* cleanup */ + semanage_context_free(con1); semanage_iface_free(iface); cleanup_handle(SH_CONNECT); } @@ -357,6 +371,8 @@ void test_iface_create(void) CU_ASSERT(semanage_iface_set_msgcon(sh, iface, msgcon) >= 0); /* cleanup */ + semanage_context_free(msgcon); + semanage_context_free(ifcon); semanage_iface_free(iface); cleanup_handle(SH_CONNECT); } @@ -393,6 +409,8 @@ void test_iface_clone(void) CU_ASSERT_CONTEXT_EQUAL(msgcon, msgcon2); /* cleanup */ + semanage_context_free(msgcon); + semanage_context_free(ifcon); semanage_iface_free(iface); semanage_iface_free(iface_clone); cleanup_handle(SH_CONNECT); @@ -426,6 +444,7 @@ void test_iface_query(void) CU_ASSERT_CONTEXT_EQUAL(con, con_exp); /* cleanup */ + semanage_iface_key_free(key); semanage_iface_free(iface); semanage_iface_free(iface_exp); cleanup_handle(SH_CONNECT); @@ -513,6 +532,8 @@ void test_iface_list(void) for (unsigned int i = 0; i < count; i++) semanage_iface_free(records[i]); + free(records); + /* cleanup */ cleanup_handle(SH_CONNECT); } @@ -541,11 +562,13 @@ void test_iface_modify_del_query_local(void) CU_ASSERT(semanage_iface_query_local(sh, key, &iface_local) >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(iface_local); + semanage_iface_free(iface_local); CU_ASSERT(semanage_iface_del_local(sh, key) >= 0); CU_ASSERT(semanage_iface_query_local(sh, key, &iface_local) < 0); /* cleanup */ + semanage_iface_key_free(key); semanage_iface_free(iface); cleanup_handle(SH_TRANS); } @@ -658,6 +681,7 @@ void test_iface_list_local(void) /* cleanup */ for (unsigned int i = 0; i < count; i++) semanage_iface_free(records[i]); + free(records); delete_local_iface(I_FIRST); delete_local_iface(I_SECOND); diff --git a/libsemanage/tests/test_node.c b/libsemanage/tests/test_node.c index 53c2eb69..e49e8c3b 100644 --- a/libsemanage/tests/test_node.c +++ b/libsemanage/tests/test_node.c @@ -148,6 +148,8 @@ semanage_node_t *get_node_nth(int idx) if (i != (unsigned int) idx) semanage_node_free(records[i]); + free(records); + return node; } @@ -167,6 +169,8 @@ semanage_node_key_t *get_node_key_nth(int idx) CU_ASSERT_FATAL(res >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(key); + semanage_node_free(node); + return key; } @@ -181,6 +185,10 @@ void add_local_node(int idx) CU_ASSERT_PTR_NOT_NULL_FATAL(key); CU_ASSERT_FATAL(semanage_node_modify_local(sh, key, node) >= 0); + + /* cleanup */ + semanage_node_key_free(key); + semanage_node_free(node); } void delete_local_node(int idx) @@ -190,6 +198,9 @@ void delete_local_node(int idx) key = get_node_key_nth(idx); CU_ASSERT_FATAL(semanage_node_del_local(sh, key) >= 0); + + /* cleanup */ + semanage_node_key_free(key); } /* Function semanage_node_compare */ @@ -305,6 +316,7 @@ void test_node_get_set_addr(void) CU_ASSERT_STRING_EQUAL(addr, "192.168.0.1"); /* cleanup */ + free(addr); semanage_node_free(node); cleanup_handle(SH_CONNECT); } @@ -334,6 +346,7 @@ void test_node_get_set_addr_bytes(void) CU_ASSERT(addr1[i] == addr2[i]); /* cleanup */ + free(addr2); semanage_node_free(node); cleanup_handle(SH_CONNECT); } @@ -357,6 +370,7 @@ void test_node_get_set_mask(void) CU_ASSERT_STRING_EQUAL(mask, "255.255.255.0"); /* cleanup */ + free(mask); semanage_node_free(node); cleanup_handle(SH_CONNECT); } @@ -386,6 +400,7 @@ void test_node_get_set_mask_bytes(void) CU_ASSERT(mask1[i] == mask2[i]); /* cleanup */ + free(mask2); semanage_node_free(node); cleanup_handle(SH_CONNECT); } @@ -436,6 +451,7 @@ void test_node_get_set_con(void) CU_ASSERT_CONTEXT_EQUAL(con1, con2); /* cleanup */ + semanage_context_free(con1); semanage_node_free(node); cleanup_handle(SH_CONNECT); } @@ -461,6 +477,7 @@ void test_node_create(void) CU_ASSERT(semanage_node_set_con(sh, node, con) >= 0); /* cleanup */ + semanage_context_free(con); semanage_node_free(node); cleanup_handle(SH_CONNECT); } @@ -508,6 +525,9 @@ void test_node_clone(void) CU_ASSERT_CONTEXT_EQUAL(con, con2); /* cleanup */ + free(mask2); + free(addr2); + semanage_context_free(con); semanage_node_free(node); semanage_node_free(node_clone); cleanup_handle(SH_CONNECT); @@ -552,6 +572,8 @@ void test_node_query(void) CU_ASSERT_CONTEXT_EQUAL(con, con_exp); /* cleanup */ + semanage_node_key_free(key); + semanage_node_free(node_exp); semanage_node_free(node); cleanup_handle(SH_CONNECT); } @@ -638,6 +660,8 @@ void test_node_list(void) for (unsigned int i = 0; i < count; i++) semanage_node_free(records[i]); + free(records); + /* cleanup */ cleanup_handle(SH_CONNECT); } @@ -679,6 +703,7 @@ void test_node_modify_del_query_local(void) CU_ASSERT(semanage_node_query_local(sh, key, &node_local) >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(node_local); + semanage_node_free(node_local); CU_ASSERT(semanage_node_del_local(sh, key) >= 0); CU_ASSERT(semanage_node_del_local(sh, key_tmp) >= 0); @@ -686,6 +711,8 @@ void test_node_modify_del_query_local(void) CU_ASSERT(semanage_node_query_local(sh, key, &node_local) < 0); /* cleanup */ + semanage_node_key_free(key_tmp); + semanage_node_key_free(key); semanage_node_free(node); semanage_node_free(node_tmp); cleanup_handle(SH_TRANS); @@ -800,6 +827,8 @@ void test_node_list_local(void) for (unsigned int i = 0; i < count; i++) semanage_node_free(records[i]); + free(records); + delete_local_node(I_FIRST); delete_local_node(I_SECOND); delete_local_node(I_THIRD); diff --git a/libsemanage/tests/test_other.c b/libsemanage/tests/test_other.c index c4ee0ed8..0a57e247 100644 --- a/libsemanage/tests/test_other.c +++ b/libsemanage/tests/test_other.c @@ -81,6 +81,9 @@ void test_semanage_context(void) assert(str); CU_ASSERT_STRING_EQUAL(str, "user_u:role_r:type_t:s0"); + semanage_context_free(con); + con = NULL; + CU_ASSERT(semanage_context_from_string(sh, "my_u:my_r:my_t:s0", &con) >= 0); CU_ASSERT_STRING_EQUAL(semanage_context_get_user(con), "my_u"); @@ -95,6 +98,7 @@ void test_semanage_context(void) CU_ASSERT_STRING_EQUAL(semanage_context_get_mls(con_clone), "s0"); /* cleanup */ + free(str); semanage_context_free(con); semanage_context_free(con_clone); cleanup_handle(SH_CONNECT); @@ -115,6 +119,8 @@ void test_debug(void) CU_ASSERT(semanage_module_info_set_priority(sh, modinfo, -42) < 0); /* cleanup */ + semanage_module_info_destroy(sh, modinfo); + free(modinfo); CU_ASSERT(semanage_disconnect(sh) >= 0); semanage_handle_destroy(sh); } diff --git a/libsemanage/tests/test_port.c b/libsemanage/tests/test_port.c index 0408be4d..f4c6ec21 100644 --- a/libsemanage/tests/test_port.c +++ b/libsemanage/tests/test_port.c @@ -146,6 +146,8 @@ semanage_port_t *get_port_nth(int idx) if (i != (unsigned int) idx) semanage_port_free(records[i]); + free(records); + return port; } @@ -165,6 +167,9 @@ semanage_port_key_t *get_port_key_nth(int idx) CU_ASSERT_FATAL(res >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(key); + /* cleanup */ + semanage_port_free(port); + return key; } @@ -181,6 +186,10 @@ void add_local_port(int port_idx) CU_ASSERT_PTR_NOT_NULL_FATAL(key); CU_ASSERT_FATAL(semanage_port_modify_local(sh, key, port) >= 0); + + /* cleanup */ + semanage_port_key_free(key); + semanage_port_free(port); } void delete_local_port(int port_idx) @@ -192,6 +201,8 @@ void delete_local_port(int port_idx) key = get_port_key_nth(port_idx); CU_ASSERT_FATAL(semanage_port_del_local(sh, key) >= 0); + + semanage_port_key_free(key); } /* Function semanage_port_compare */ @@ -447,6 +458,7 @@ void test_port_clone(void) CU_ASSERT_CONTEXT_EQUAL(con, con2); /* cleanup */ + semanage_context_free(con); semanage_port_free(port); semanage_port_free(port_clone); cleanup_handle(SH_CONNECT); @@ -480,6 +492,7 @@ void test_port_query(void) CU_ASSERT_CONTEXT_EQUAL(con, con_exp); /* cleanup */ + semanage_port_key_free(key); semanage_port_free(port); semanage_port_free(port_exp); cleanup_handle(SH_CONNECT); @@ -567,6 +580,8 @@ void test_port_list(void) for (unsigned int i = 0; i < count; i++) semanage_port_free(records[i]); + free(records); + cleanup_handle(SH_CONNECT); } @@ -594,11 +609,14 @@ void test_port_modify_del_local(void) con_local = semanage_port_get_con(port_local); CU_ASSERT_CONTEXT_EQUAL(con, con_local); + semanage_port_free(port_local); CU_ASSERT(semanage_port_del_local(sh, key) >= 0); CU_ASSERT(semanage_port_query_local(sh, key, &port_local) < 0); /* cleanup */ + semanage_context_free(con); + semanage_port_key_free(key); semanage_port_free(port); cleanup_handle(SH_TRANS); } @@ -633,6 +651,7 @@ void test_port_query_local(void) /* cleanup */ delete_local_port(I_FIRST); + semanage_port_key_free(key); semanage_port_free(port); semanage_port_free(port_exp); cleanup_handle(SH_TRANS); @@ -747,6 +766,8 @@ void test_port_list_local(void) for (unsigned int i = 0; i < count; i++) semanage_port_free(records[i]); + free(records); + delete_local_port(I_FIRST); delete_local_port(I_SECOND); delete_local_port(I_THIRD); @@ -773,6 +794,7 @@ void helper_port_validate_local_noport(void) helper_commit(); /* cleanup */ + semanage_port_key_free(key); helper_begin_transaction(); delete_local_port(I_FIRST); cleanup_handle(SH_TRANS); @@ -832,6 +854,8 @@ void helper_port_validate_local_twoports(void) helper_begin_transaction(); CU_ASSERT(semanage_port_del_local(sh, key1) >= 0); CU_ASSERT(semanage_port_del_local(sh, key2) >= 0); + semanage_context_free(con2); + semanage_context_free(con1); semanage_port_key_free(key1); semanage_port_key_free(key2); semanage_port_free(port1); diff --git a/libsemanage/tests/test_user.c b/libsemanage/tests/test_user.c index cd082030..c3835c8d 100644 --- a/libsemanage/tests/test_user.c +++ b/libsemanage/tests/test_user.c @@ -130,6 +130,8 @@ semanage_user_t *get_user_nth(int idx) if (i != (unsigned int) idx) semanage_user_free(records[i]); + free(records); + return user; } @@ -149,6 +151,8 @@ semanage_user_key_t *get_user_key_nth(int idx) CU_ASSERT_FATAL(res >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(key); + semanage_user_free(user); + return key; } @@ -165,6 +169,9 @@ void add_local_user(int user_idx) CU_ASSERT_PTR_NOT_NULL_FATAL(key); CU_ASSERT_FATAL(semanage_user_modify_local(sh, key, user) >= 0); + + semanage_user_key_free(key); + semanage_user_free(user); } void delete_local_user(int user_idx) @@ -176,6 +183,8 @@ void delete_local_user(int user_idx) key = get_user_key_nth(user_idx); CU_ASSERT_FATAL(semanage_user_del_local(sh, key) >= 0); + + semanage_user_key_free(key); } /* Function semanage_user_compare */ @@ -391,6 +400,7 @@ void test_user_roles(void) CU_ASSERT(semanage_user_get_num_roles(user) == 0); /* cleanup */ + free(roles_arr); semanage_user_free(user); cleanup_handle(SH_CONNECT); } @@ -459,6 +469,7 @@ void test_user_query(void) CU_ASSERT_PTR_NOT_NULL(user); /* cleanup */ + semanage_user_key_free(key); semanage_user_free(user); cleanup_handle(SH_CONNECT); } @@ -546,6 +557,8 @@ void test_user_list(void) for (unsigned int i = 0; i < count; i++) semanage_user_free(records[i]); + free(records); + cleanup_handle(SH_CONNECT); } @@ -573,10 +586,12 @@ void test_user_modify_del_query_local(void) CU_ASSERT(semanage_user_query_local(sh, key, &user_local) >= 0); CU_ASSERT_PTR_NOT_NULL_FATAL(user_local); + semanage_user_free(user_local); CU_ASSERT(semanage_user_del_local(sh, key) >= 0); CU_ASSERT(semanage_user_query_local(sh, key, &user_local) < 0); /* cleanup */ + semanage_user_key_free(key); semanage_user_free(user); cleanup_handle(SH_TRANS); } @@ -683,6 +698,8 @@ void test_user_list_local(void) for (unsigned int i = 0; i < count; i++) semanage_user_free(records[i]); + free(records); + delete_local_user(I_FIRST); delete_local_user(I_SECOND); delete_local_user(I_THIRD); diff --git a/libsemanage/tests/utilities.c b/libsemanage/tests/utilities.c index 18393215..b28ae155 100644 --- a/libsemanage/tests/utilities.c +++ b/libsemanage/tests/utilities.c @@ -99,6 +99,7 @@ int write_test_policy_from_file(const char *filename) { char *buf = NULL; size_t len = 0; FILE *fptr = fopen(filename, "rb"); + int rc; if (!fptr) { perror("fopen"); @@ -120,7 +121,9 @@ int write_test_policy_from_file(const char *filename) { fread(buf, len, 1, fptr); fclose(fptr); - return write_test_policy(buf, len); + rc = write_test_policy(buf, len); + free(buf); + return rc; } int write_test_policy_src(unsigned char *data, unsigned int data_len) { diff --git a/libsemanage/tests/utilities.h b/libsemanage/tests/utilities.h index db4dabf9..298b3280 100644 --- a/libsemanage/tests/utilities.h +++ b/libsemanage/tests/utilities.h @@ -39,6 +39,8 @@ CU_ASSERT(semanage_context_to_string(sh, CON1, &__str) >= 0); \ CU_ASSERT(semanage_context_to_string(sh, CON2, &__str2) >= 0); \ CU_ASSERT_STRING_EQUAL(__str, __str2); \ + free(__str2); \ + free(__str); \ } while (0) diff --git a/libsepol/Android.bp b/libsepol/Android.bp index 5232a68a..6135745c 100644 --- a/libsepol/Android.bp +++ b/libsepol/Android.bp @@ -33,21 +33,32 @@ license { ], } -common_CFLAGS = [ - "-D_GNU_SOURCE", - "-Wall", - "-Werror", - "-W", - "-Wundef", - "-Wshadow", - "-Wno-error=missing-noreturn", - "-Wmissing-format-attribute", -] +cc_defaults { + name: "libsepol_defaults", + cflags: [ + "-D_GNU_SOURCE", + "-Wall", + "-Werror", + "-W", + "-Wundef", + "-Wshadow", + "-Wno-error=missing-noreturn", + "-Wmissing-format-attribute", + ], + target: { + bionic: { + cflags: ["-DHAVE_REALLOCARRAY"] + }, + musl: { + cflags: ["-DHAVE_REALLOCARRAY"] + } + } +} cc_library { name: "libsepol", + defaults: ["libsepol_defaults"], host_supported: true, - cflags: common_CFLAGS, srcs: [ "src/assertion.c", "src/avrule_block.c", @@ -135,7 +146,7 @@ cc_library { cc_binary_host { name: "chkcon", + defaults: ["libsepol_defaults"], srcs: ["utils/chkcon.c"], shared_libs: ["libsepol"], - cflags: common_CFLAGS, } diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c index 4cc7f87f..38edcf8e 100644 --- a/libsepol/cil/src/cil.c +++ b/libsepol/cil/src/cil.c @@ -1456,6 +1456,12 @@ int cil_userprefixes_to_string(struct cil_db *db, char **out, size_t *size) buf_pos = snprintf(str_tmp, str_len, "user %s prefix %s;\n", user->datum.fqn, userprefix->prefix_str); + if (buf_pos < 0) { + free(str_tmp); + *size = 0; + *out = NULL; + goto exit; + } str_len -= buf_pos; str_tmp += buf_pos; } @@ -1765,6 +1771,9 @@ int cil_filecons_to_string(struct cil_db *db, char **out, size_t *size) str_tmp += buf_pos; switch(filecon->type) { + case CIL_FILECON_ANY: + str_type = ""; + break; case CIL_FILECON_FILE: str_type = "\t--"; break; @@ -2530,7 +2539,7 @@ void cil_filecon_init(struct cil_filecon **filecon) *filecon = cil_malloc(sizeof(**filecon)); (*filecon)->path_str = NULL; - (*filecon)->type = 0; + (*filecon)->type = CIL_FILECON_ANY; (*filecon)->context_str = NULL; (*filecon)->context = NULL; } @@ -2574,6 +2583,7 @@ void cil_genfscon_init(struct cil_genfscon **genfscon) (*genfscon)->fs_str = NULL; (*genfscon)->path_str = NULL; + (*genfscon)->file_type = CIL_FILECON_ANY; (*genfscon)->context_str = NULL; (*genfscon)->context = NULL; } diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c index d8aa495a..53017e2d 100644 --- a/libsepol/cil/src/cil_binary.c +++ b/libsepol/cil/src/cil_binary.c @@ -2823,6 +2823,12 @@ int cil_constrain_to_policydb_helper(policydb_t *pdb, const struct cil_db *db, s goto exit; } + if (sepol_constrain->permissions == 0) { + /* No permissions, so don't insert rule. */ + free(sepol_constrain); + return SEPOL_OK; + } + rc = __cil_constrain_expr_to_sepol_expr(pdb, db, expr, &sepol_expr); if (rc != SEPOL_OK) { goto exit; @@ -3462,6 +3468,43 @@ int cil_genfscon_to_policydb(policydb_t *pdb, struct cil_sort *genfscons) new_ocon->u.name = cil_strdup(cil_genfscon->path_str); + if (cil_genfscon->file_type != CIL_FILECON_ANY) { + class_datum_t *class_datum; + const char *class_name; + switch (cil_genfscon->file_type) { + case CIL_FILECON_FILE: + class_name = "file"; + break; + case CIL_FILECON_DIR: + class_name = "dir"; + break; + case CIL_FILECON_CHAR: + class_name = "chr_file"; + break; + case CIL_FILECON_BLOCK: + class_name = "blk_file"; + break; + case CIL_FILECON_SOCKET: + class_name = "sock_file"; + break; + case CIL_FILECON_PIPE: + class_name = "fifo_file"; + break; + case CIL_FILECON_SYMLINK: + class_name = "lnk_file"; + break; + default: + rc = SEPOL_ERR; + goto exit; + } + class_datum = hashtab_search(pdb->p_classes.table, class_name); + if (!class_datum) { + rc = SEPOL_ERR; + goto exit; + } + new_ocon->v.sclass = class_datum->s.value; + } + rc = __cil_context_to_sepol_context(pdb, cil_genfscon->context, &new_ocon->context[0]); if (rc != SEPOL_OK) { goto exit; @@ -4603,6 +4646,9 @@ static int __cil_print_neverallow_failure(const struct cil_db *db, struct cil_tr char *neverallow_str; char *allow_str; enum cil_flavor avrule_flavor; + int num_matching = 0; + int count_matching = 0; + enum cil_log_level log_level = cil_get_log_level(); target.rule_kind = CIL_AVRULE_ALLOWED; target.is_extended = cil_rule->is_extended; @@ -4630,10 +4676,18 @@ static int __cil_print_neverallow_failure(const struct cil_db *db, struct cil_tr } cil_list_for_each(i2, matching) { + num_matching++; + } + cil_list_for_each(i2, matching) { n2 = i2->data; r2 = n2->data; __cil_print_parents(" ", n2); __cil_print_rule(" ", allow_str, r2); + count_matching++; + if (count_matching >= 4 && num_matching > 4 && log_level == CIL_ERR) { + cil_log(CIL_ERR, " Only first 4 of %d matching rules shown (use \"-v\" to show all)\n", num_matching); + break; + } } cil_log(CIL_ERR,"\n"); cil_list_destroy(&matching, CIL_FALSE); @@ -4826,6 +4880,7 @@ static int cil_check_type_bounds(const struct cil_db *db, policydb_t *pdb, void struct cil_avrule target; struct cil_tree_node *n1 = NULL; int count_bad = 0; + enum cil_log_level log_level = cil_get_log_level(); *violation = CIL_TRUE; @@ -4872,16 +4927,16 @@ static int cil_check_type_bounds(const struct cil_db *db, policydb_t *pdb, void __cil_print_rule(" ", "allow", r2); } count_matching++; - if (count_matching >= 2) { - cil_log(CIL_ERR, " Only first 2 of %d matching rules shown\n", num_matching); + if (count_matching >= 2 && num_matching > 2 && log_level == CIL_ERR) { + cil_log(CIL_ERR, " Only first 2 of %d matching rules shown (use \"-v\" to show all)\n", num_matching); break; } } cil_list_destroy(&matching, CIL_FALSE); cil_list_destroy(&target.perms.classperms, CIL_TRUE); count_bad++; - if (count_bad >= 2) { - cil_log(CIL_ERR, " Only first 2 of %d bad rules shown\n", numbad); + if (count_bad >= 4 && numbad > 4 && log_level == CIL_ERR) { + cil_log(CIL_ERR, " Only first 4 of %d bad rules shown (use \"-v\" to show all)\n", numbad); break; } } diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c index 9c34be23..5f9392d1 100644 --- a/libsepol/cil/src/cil_build_ast.c +++ b/libsepol/cil/src/cil_build_ast.c @@ -4229,7 +4229,9 @@ int cil_gen_filecon(struct cil_db *db, struct cil_tree_node *parse_current, stru filecon->path_str = parse_current->next->data; - if (type == CIL_KEY_FILE) { + if (type == CIL_KEY_ANY) { + filecon->type = CIL_FILECON_ANY; + } else if (type == CIL_KEY_FILE) { filecon->type = CIL_FILECON_FILE; } else if (type == CIL_KEY_DIR) { filecon->type = CIL_FILECON_DIR; @@ -4243,8 +4245,6 @@ int cil_gen_filecon(struct cil_db *db, struct cil_tree_node *parse_current, stru filecon->type = CIL_FILECON_PIPE; } else if (type == CIL_KEY_SYMLINK) { filecon->type = CIL_FILECON_SYMLINK; - } else if (type == CIL_KEY_ANY) { - filecon->type = CIL_FILECON_ANY; } else { cil_log(CIL_ERR, "Invalid file type\n"); rc = SEPOL_ERR; @@ -4572,9 +4572,11 @@ int cil_gen_genfscon(struct cil_db *db, struct cil_tree_node *parse_current, str CIL_SYN_STRING, CIL_SYN_STRING, CIL_SYN_STRING | CIL_SYN_LIST, + CIL_SYN_STRING | CIL_SYN_LIST | CIL_SYN_END, CIL_SYN_END }; size_t syntax_len = sizeof(syntax)/sizeof(*syntax); + struct cil_tree_node *context_node; int rc = SEPOL_ERR; struct cil_genfscon *genfscon = NULL; @@ -4592,15 +4594,48 @@ int cil_gen_genfscon(struct cil_db *db, struct cil_tree_node *parse_current, str genfscon->fs_str = parse_current->next->data; genfscon->path_str = parse_current->next->next->data; - if (parse_current->next->next->next->cl_head == NULL ) { - genfscon->context_str = parse_current->next->next->next->data; + if (parse_current->next->next->next->next) { + /* (genfscon <FS_STR> <PATH_STR> <FILE_TYPE> ... */ + char *file_type = parse_current->next->next->next->data; + if (file_type == CIL_KEY_ANY) { + genfscon->file_type = CIL_FILECON_ANY; + } else if (file_type == CIL_KEY_FILE) { + genfscon->file_type = CIL_FILECON_FILE; + } else if (file_type == CIL_KEY_DIR) { + genfscon->file_type = CIL_FILECON_DIR; + } else if (file_type == CIL_KEY_CHAR) { + genfscon->file_type = CIL_FILECON_CHAR; + } else if (file_type == CIL_KEY_BLOCK) { + genfscon->file_type = CIL_FILECON_BLOCK; + } else if (file_type == CIL_KEY_SOCKET) { + genfscon->file_type = CIL_FILECON_SOCKET; + } else if (file_type == CIL_KEY_PIPE) { + genfscon->file_type = CIL_FILECON_PIPE; + } else if (file_type == CIL_KEY_SYMLINK) { + genfscon->file_type = CIL_FILECON_SYMLINK; + } else { + if (parse_current->next->next->next->cl_head) { + cil_log(CIL_ERR, "Expecting file type, but found a list\n"); + } else { + cil_log(CIL_ERR, "Invalid file type \"%s\"\n", file_type); + } + rc = SEPOL_ERR; + goto exit; + } + context_node = parse_current->next->next->next->next; } else { - cil_context_init(&genfscon->context); + /* (genfscon <FS_STR> <PATH_STR> ... */ + context_node = parse_current->next->next->next; + } - rc = cil_fill_context(parse_current->next->next->next->cl_head, genfscon->context); + if (context_node->cl_head) { + cil_context_init(&genfscon->context); + rc = cil_fill_context(context_node->cl_head, genfscon->context); if (rc != SEPOL_OK) { goto exit; } + } else { + genfscon->context_str = context_node->data; } ast_node->data = genfscon; @@ -5668,10 +5703,10 @@ int cil_fill_ipaddr(struct cil_tree_node *addr_node, struct cil_ipaddr *addr) goto exit; } - if (strchr(addr_node->data, '.') != NULL) { - addr->family = AF_INET; - } else { + if (strchr(addr_node->data, ':') != NULL) { addr->family = AF_INET6; + } else { + addr->family = AF_INET; } rc = inet_pton(addr->family, addr_node->data, &addr->ip); @@ -5683,7 +5718,7 @@ int cil_fill_ipaddr(struct cil_tree_node *addr_node, struct cil_ipaddr *addr) return SEPOL_OK; exit: - cil_log(CIL_ERR, "Bad ip address or netmask\n"); + cil_log(CIL_ERR, "Bad ip address or netmask: %s\n", (addr_node && addr_node->data) ? (const char *)addr_node->data : "n/a"); return rc; } diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c index 2fad972c..a4ead9db 100644 --- a/libsepol/cil/src/cil_copy_ast.c +++ b/libsepol/cil/src/cil_copy_ast.c @@ -1725,6 +1725,12 @@ int __cil_copy_node_helper(struct cil_tree_node *orig, uint32_t *finished, void copy_func = &cil_copy_block; break; case CIL_BLOCKABSTRACT: + if (args->orig_dest->flavor == CIL_BLOCKINHERIT) { + /* When inheriting a block, don't copy any blockabstract + * statements. Inheriting a block from a block that was + * just inherited never worked. */ + return SEPOL_OK; + } copy_func = &cil_copy_blockabstract; break; case CIL_BLOCKINHERIT: diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h index 6f1d3cb5..a7604762 100644 --- a/libsepol/cil/src/cil_internal.h +++ b/libsepol/cil/src/cil_internal.h @@ -730,14 +730,14 @@ struct cil_context { }; enum cil_filecon_types { - CIL_FILECON_FILE = 1, + CIL_FILECON_ANY = 0, + CIL_FILECON_FILE, CIL_FILECON_DIR, CIL_FILECON_CHAR, CIL_FILECON_BLOCK, CIL_FILECON_SOCKET, CIL_FILECON_PIPE, CIL_FILECON_SYMLINK, - CIL_FILECON_ANY }; struct cil_filecon { @@ -791,6 +791,7 @@ struct cil_ipaddr { struct cil_genfscon { char *fs_str; char *path_str; + enum cil_filecon_types file_type; char *context_str; struct cil_context *context; }; diff --git a/libsepol/cil/src/cil_log.c b/libsepol/cil/src/cil_log.c index a8e4d2e9..a296929b 100644 --- a/libsepol/cil/src/cil_log.c +++ b/libsepol/cil/src/cil_log.c @@ -70,3 +70,8 @@ void cil_set_log_level(enum cil_log_level lvl) { cil_log_level = lvl; } + +enum cil_log_level cil_get_log_level(void) +{ + return cil_log_level; +} diff --git a/libsepol/cil/src/cil_log.h b/libsepol/cil/src/cil_log.h index 541569be..442781fb 100644 --- a/libsepol/cil/src/cil_log.h +++ b/libsepol/cil/src/cil_log.h @@ -38,4 +38,6 @@ __attribute__ ((format(printf, 2, 0))) void cil_vlog(enum cil_log_level lvl, const char *msg, va_list args); __attribute__ ((format(printf, 2, 3))) void cil_log(enum cil_log_level lvl, const char *msg, ...); +enum cil_log_level cil_get_log_level(void); + #endif // CIL_LOG_H_ diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c index 7e2c2b9a..09c02af9 100644 --- a/libsepol/cil/src/cil_post.c +++ b/libsepol/cil/src/cil_post.c @@ -2280,8 +2280,10 @@ static int __cil_post_report_conflict(struct cil_tree_node *node, uint32_t *fini static int __cil_post_process_context_rules(struct cil_sort *sort, int (*compar)(const void *, const void *), int (*concompar)(const void *, const void *), struct cil_db *db, enum cil_flavor flavor, const char *flavor_str) { uint32_t count = sort->count; - uint32_t i, j = 0, removed = 0; + uint32_t i = 0, j, removed = 0; + int conflicting = 0; int rc = SEPOL_OK; + enum cil_log_level log_level = cil_get_log_level(); if (count < 2) { return SEPOL_OK; @@ -2289,36 +2291,43 @@ static int __cil_post_process_context_rules(struct cil_sort *sort, int (*compar) qsort(sort->array, sort->count, sizeof(sort->array), compar); - for (i=1; i<count; i++) { + for (j=1; j<count; j++) { if (compar(&sort->array[i], &sort->array[j]) != 0) { - j++; + i++; + if (conflicting >= 4) { + /* 2 rules were written when conflicting == 1 */ + cil_log(CIL_WARN, " Only first 4 of %d conflicting rules shown\n", conflicting); + } + conflicting = 0; } else { removed++; - if (!db->multiple_decls || - concompar(&sort->array[i], &sort->array[j]) != 0) { - struct cil_list_item li; - int rc2; - cil_log(CIL_WARN, "Found conflicting %s rules\n", - flavor_str); - rc = SEPOL_ERR; - li.flavor = flavor; - li.data = sort->array[i]; - rc2 = cil_tree_walk(db->ast->root, - __cil_post_report_conflict, - NULL, NULL, &li); - if (rc2 != SEPOL_OK) goto exit; - li.data = sort->array[j]; - rc2 = cil_tree_walk(db->ast->root, - __cil_post_report_conflict, - NULL, NULL, &li); - if (rc2 != SEPOL_OK) goto exit; + if (!db->multiple_decls || concompar(&sort->array[i], &sort->array[j]) != 0) { + conflicting++; + if (log_level >= CIL_WARN) { + struct cil_list_item li; + int rc2; + li.flavor = flavor; + if (conflicting == 1) { + cil_log(CIL_WARN, "Found conflicting %s rules\n", flavor_str); + rc = SEPOL_ERR; + li.data = sort->array[i]; + rc2 = cil_tree_walk(db->ast->root, __cil_post_report_conflict, + NULL, NULL, &li); + if (rc2 != SEPOL_OK) goto exit; + } + if (conflicting < 4 || log_level > CIL_WARN) { + li.data = sort->array[j]; + rc2 = cil_tree_walk(db->ast->root, __cil_post_report_conflict, + NULL, NULL, &li); + if (rc2 != SEPOL_OK) goto exit; + } + } } } - if (i != j) { - sort->array[j] = sort->array[i]; + if (i != j && !conflicting) { + sort->array[i] = sort->array[j]; } } - sort->count = count - removed; exit: diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c index e97a9f46..69a8a2ed 100644 --- a/libsepol/cil/src/cil_resolve_ast.c +++ b/libsepol/cil/src/cil_resolve_ast.c @@ -65,6 +65,7 @@ struct cil_args_resolve { struct cil_list *sensitivityorder_lists; struct cil_list *in_list_before; struct cil_list *in_list_after; + struct cil_list *abstract_blocks; }; static struct cil_name * __cil_insert_name(struct cil_db *db, hashtab_key_t key, struct cil_tree_node *ast_node) @@ -754,6 +755,11 @@ int cil_resolve_classcommon(struct cil_tree_node *current, void *extra_args) if (rc != SEPOL_OK) { goto exit; } + if (NODE(class_datum)->flavor != CIL_CLASS) { + cil_log(CIL_ERR, "Class %s is not a kernel class and cannot be associated with common %s\n", clscom->class_str, clscom->common_str); + rc = SEPOL_ERR; + goto exit; + } rc = cil_resolve_name(current, clscom->common_str, CIL_SYM_COMMONS, extra_args, &common_datum); if (rc != SEPOL_OK) { @@ -2379,11 +2385,25 @@ exit: return rc; } +static void cil_mark_subtree_abstract(struct cil_tree_node *node) +{ + struct cil_block *block = node->data; + + block->is_abstract = CIL_TRUE; + + for (node = node->cl_head; node; node = node->next) { + if (node->flavor == CIL_BLOCK) { + cil_mark_subtree_abstract(node); + } + } +} + int cil_resolve_blockabstract(struct cil_tree_node *current, void *extra_args) { struct cil_blockabstract *abstract = current->data; struct cil_symtab_datum *block_datum = NULL; struct cil_tree_node *block_node = NULL; + struct cil_args_resolve *args = extra_args; int rc = SEPOL_ERR; rc = cil_resolve_name(current, abstract->block_str, CIL_SYM_BLOCKS, extra_args, &block_datum); @@ -2398,7 +2418,7 @@ int cil_resolve_blockabstract(struct cil_tree_node *current, void *extra_args) goto exit; } - ((struct cil_block*)block_datum)->is_abstract = CIL_TRUE; + cil_list_append(args->abstract_blocks, CIL_NODE, block_node); return SEPOL_OK; @@ -4084,6 +4104,7 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current) extra_args.sensitivityorder_lists = NULL; extra_args.in_list_before = NULL; extra_args.in_list_after = NULL; + extra_args.abstract_blocks = NULL; cil_list_init(&extra_args.to_destroy, CIL_NODE); cil_list_init(&extra_args.sidorder_lists, CIL_LIST_ITEM); @@ -4093,6 +4114,7 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current) cil_list_init(&extra_args.sensitivityorder_lists, CIL_LIST_ITEM); cil_list_init(&extra_args.in_list_before, CIL_IN); cil_list_init(&extra_args.in_list_after, CIL_IN); + cil_list_init(&extra_args.abstract_blocks, CIL_NODE); for (pass = CIL_PASS_TIF; pass < CIL_PASS_NUM; pass++) { extra_args.pass = pass; @@ -4116,6 +4138,13 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current) cil_list_destroy(&extra_args.in_list_after, CIL_FALSE); } + if (pass == CIL_PASS_BLKABS) { + struct cil_list_item *item; + cil_list_for_each(item, extra_args.abstract_blocks) { + cil_mark_subtree_abstract(item->data); + } + } + if (pass == CIL_PASS_BLKIN_LINK) { rc = cil_check_for_bad_inheritance(current); if (rc != SEPOL_OK) { @@ -4234,6 +4263,7 @@ exit: cil_list_destroy(&extra_args.to_destroy, CIL_FALSE); cil_list_destroy(&extra_args.in_list_before, CIL_FALSE); cil_list_destroy(&extra_args.in_list_after, CIL_FALSE); + cil_list_destroy(&extra_args.abstract_blocks, CIL_FALSE); return rc; } @@ -4255,9 +4285,13 @@ static int __cil_resolve_name_with_parents(struct cil_tree_node *node, char *nam case CIL_ROOT: goto exit; break; - case CIL_BLOCK: - symtab = &((struct cil_block*)node->data)->symtab[sym_index]; - rc = cil_symtab_get_datum(symtab, name, datum); + case CIL_BLOCK: { + struct cil_block *block = node->data; + if (!block->is_abstract) { + symtab = &block->symtab[sym_index]; + rc = cil_symtab_get_datum(symtab, name, datum); + } + } break; case CIL_BLOCKINHERIT: { struct cil_blockinherit *inherit = node->data; diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c index 38374f10..c0ee7473 100644 --- a/libsepol/cil/src/cil_write_ast.c +++ b/libsepol/cil/src/cil_write_ast.c @@ -1232,24 +1232,34 @@ void cil_write_ast_node(FILE *out, struct cil_tree_node *node) struct cil_filecon *filecon = node->data; fprintf(out, "(filecon "); fprintf(out, "\"%s\" ", filecon->path_str); - if (filecon->type == CIL_FILECON_FILE) + switch (filecon->type) { + case CIL_FILECON_ANY: + fprintf(out, "%s ", CIL_KEY_ANY); + break; + case CIL_FILECON_FILE: fprintf(out, "%s ", CIL_KEY_FILE); - else if (filecon->type == CIL_FILECON_DIR) + break; + case CIL_FILECON_DIR: fprintf(out, "%s ", CIL_KEY_DIR); - else if (filecon->type == CIL_FILECON_CHAR) + break; + case CIL_FILECON_CHAR: fprintf(out, "%s ", CIL_KEY_CHAR); - else if (filecon->type == CIL_FILECON_BLOCK) + break; + case CIL_FILECON_BLOCK: fprintf(out, "%s ", CIL_KEY_BLOCK); - else if (filecon->type == CIL_FILECON_SOCKET) + break; + case CIL_FILECON_SOCKET: fprintf(out, "%s ", CIL_KEY_SOCKET); - else if (filecon->type == CIL_FILECON_PIPE) + break; + case CIL_FILECON_PIPE: fprintf(out, "%s ", CIL_KEY_PIPE); - else if (filecon->type == CIL_FILECON_SYMLINK) + break; + case CIL_FILECON_SYMLINK: fprintf(out, "%s ", CIL_KEY_SYMLINK); - else if (filecon->type == CIL_FILECON_ANY) - fprintf(out, "%s ", CIL_KEY_ANY); - else + break; + default: fprintf(out, "<?FILETYPE> "); + } if (filecon->context) write_context(out, filecon->context, CIL_TRUE); else if (filecon->context_str) @@ -1318,6 +1328,33 @@ void cil_write_ast_node(FILE *out, struct cil_tree_node *node) struct cil_genfscon *genfscon = node->data; fprintf(out, "(genfscon "); fprintf(out, "%s \"%s\" ", genfscon->fs_str, genfscon->path_str); + if (genfscon->file_type != CIL_FILECON_ANY) { + switch (genfscon->file_type) { + case CIL_FILECON_FILE: + fprintf(out, "%s ", CIL_KEY_FILE); + break; + case CIL_FILECON_DIR: + fprintf(out, "%s ", CIL_KEY_DIR); + break; + case CIL_FILECON_CHAR: + fprintf(out, "%s ", CIL_KEY_CHAR); + break; + case CIL_FILECON_BLOCK: + fprintf(out, "%s ", CIL_KEY_BLOCK); + break; + case CIL_FILECON_SOCKET: + fprintf(out, "%s ", CIL_KEY_SOCKET); + break; + case CIL_FILECON_PIPE: + fprintf(out, "%s ", CIL_KEY_PIPE); + break; + case CIL_FILECON_SYMLINK: + fprintf(out, "%s ", CIL_KEY_SYMLINK); + break; + default: + fprintf(out, "<?FILETYPE> "); + } + } if (genfscon->context) write_context(out, genfscon->context, CIL_TRUE); else diff --git a/libsepol/fuzz/binpolicy-fuzzer.c b/libsepol/fuzz/binpolicy-fuzzer.c new file mode 100644 index 00000000..85c59645 --- /dev/null +++ b/libsepol/fuzz/binpolicy-fuzzer.c @@ -0,0 +1,63 @@ +#include <sepol/debug.h> +#include <sepol/kernel_to_cil.h> +#include <sepol/kernel_to_conf.h> +#include <sepol/policydb/policydb.h> + +extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +static int write_binary_policy(policydb_t *p, FILE *outfp) +{ + struct policy_file pf; + + policy_file_init(&pf); + pf.type = PF_USE_STDIO; + pf.fp = outfp; + return policydb_write(p, &pf); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + policydb_t policydb = {}; + sidtab_t sidtab = {}; + struct policy_file pf; + FILE *devnull = NULL; + + sepol_debug(0); + + policy_file_init(&pf); + pf.type = PF_USE_MEMORY; + pf.data = (char *) data; + pf.len = size; + + if (policydb_init(&policydb)) + goto exit; + + if (policydb_read(&policydb, &pf, /*verbose=*/0)) + goto exit; + + if (policydb_load_isids(&policydb, &sidtab)) + goto exit; + + if (policydb.policy_type == POLICY_KERN) + (void) policydb_optimize(&policydb); + + devnull = fopen("/dev/null", "w"); + if (!devnull) + goto exit; + + (void) write_binary_policy(&policydb, devnull); + + (void) sepol_kernel_policydb_to_conf(devnull, &policydb); + + (void) sepol_kernel_policydb_to_cil(devnull, &policydb); + +exit: + if (devnull != NULL) + fclose(devnull); + + policydb_destroy(&policydb); + sepol_sidtab_destroy(&sidtab); + + /* Non-zero return values are reserved for future use. */ + return 0; +} diff --git a/libsepol/fuzz/policy.bin b/libsepol/fuzz/policy.bin Binary files differnew file mode 100644 index 00000000..6f977ef3 --- /dev/null +++ b/libsepol/fuzz/policy.bin diff --git a/libsepol/fuzz/secilc-fuzzer.c b/libsepol/fuzz/secilc-fuzzer.c index 255b3241..9a1a16de 100644 --- a/libsepol/fuzz/secilc-fuzzer.c +++ b/libsepol/fuzz/secilc-fuzzer.c @@ -8,6 +8,10 @@ #include <sepol/cil/cil.h> #include <sepol/policydb.h> +static void log_handler(__attribute__((unused)) int lvl, __attribute__((unused)) const char *msg) { + /* be quiet */ +} + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { enum cil_log_level log_level = CIL_ERR; struct sepol_policy_file *pf = NULL; @@ -24,6 +28,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { sepol_policydb_t *pdb = NULL; cil_set_log_level(log_level); + cil_set_log_handler(log_handler); cil_db_init(&db); cil_set_disable_dontaudit(db, disable_dontaudit); diff --git a/libsepol/include/sepol/policydb/polcaps.h b/libsepol/include/sepol/policydb/polcaps.h index 40669fb5..f5e32e60 100644 --- a/libsepol/include/sepol/policydb/polcaps.h +++ b/libsepol/include/sepol/policydb/polcaps.h @@ -7,16 +7,17 @@ extern "C" { /* Policy capabilities */ enum { - POLICYDB_CAPABILITY_NETPEER, - POLICYDB_CAPABILITY_OPENPERM, - POLICYDB_CAPABILITY_EXTSOCKCLASS, - POLICYDB_CAPABILITY_ALWAYSNETWORK, - POLICYDB_CAPABILITY_CGROUPSECLABEL, - POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION, - POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS, - __POLICYDB_CAPABILITY_MAX + POLICYDB_CAP_NETPEER, + POLICYDB_CAP_OPENPERM, + POLICYDB_CAP_EXTSOCKCLASS, + POLICYDB_CAP_ALWAYSNETWORK, + POLICYDB_CAP_CGROUPSECLABEL, + POLICYDB_CAP_NNP_NOSUID_TRANSITION, + POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS, + POLICYDB_CAP_IOCTL_SKIP_CLOEXEC, + __POLICYDB_CAP_MAX }; -#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) +#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1) /* Convert a capability name to number. */ extern int sepol_polcap_getnum(const char *name); diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile index dc8b1773..13410c67 100644 --- a/libsepol/src/Makefile +++ b/libsepol/src/Makefile @@ -29,6 +29,12 @@ LOBJS += $(sort $(patsubst %.c,%.lo,$(sort $(wildcard $(CILDIR)/src/*.c)) $(CIL_ override CFLAGS += -I$(CILDIR)/include endif +# check for reallocarray(3) availability +H := \# +ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) +override CFLAGS += -DHAVE_REALLOCARRAY +endif + LD_SONAME_FLAGS=-soname,$(LIBSO),--version-script=$(LIBMAP),-z,defs LN=ln diff --git a/libsepol/src/assertion.c b/libsepol/src/assertion.c index dd2749a0..161874c3 100644 --- a/libsepol/src/assertion.c +++ b/libsepol/src/assertion.c @@ -36,13 +36,21 @@ struct avtab_match_args { unsigned long errors; }; +static const char* policy_name(policydb_t *p) { + const char *policy_file = "policy.conf"; + if (p->name) { + policy_file = p->name; + } + return policy_file; +} + static void report_failure(sepol_handle_t *handle, policydb_t *p, const avrule_t *avrule, unsigned int stype, unsigned int ttype, const class_perm_node_t *curperm, uint32_t perms) { if (avrule->source_filename) { - ERR(handle, "neverallow on line %lu of %s (or line %lu of policy.conf) violated by allow %s %s:%s {%s };", - avrule->source_line, avrule->source_filename, avrule->line, + ERR(handle, "neverallow on line %lu of %s (or line %lu of %s) violated by allow %s %s:%s {%s };", + avrule->source_line, avrule->source_filename, avrule->line, policy_name(p), p->p_type_val_to_name[stype], p->p_type_val_to_name[ttype], p->p_class_val_to_name[curperm->tclass - 1], @@ -65,14 +73,11 @@ static void report_failure(sepol_handle_t *handle, policydb_t *p, const avrule_t static int match_any_class_permissions(class_perm_node_t *cp, uint32_t class, uint32_t data) { for (; cp; cp = cp->next) { - if ((cp->tclass == class) && (cp->data & data)) { - break; - } + if ((cp->tclass == class) && (cp->data & data)) + return 1; } - if (!cp) - return 0; - return 1; + return 0; } static int extended_permissions_and(uint32_t *perms1, uint32_t *perms2) { @@ -151,15 +156,16 @@ static int report_assertion_extended_permissions(sepol_handle_t *handle, ebitmap_t *tattr = &p->type_attr_map[ttype]; ebitmap_node_t *snode, *tnode; unsigned int i, j; - int rc = 1; - int ret = 0; + int rc; + int found_xperm = 0; + int errors = 0; memcpy(&tmp_key, k, sizeof(avtab_key_t)); tmp_key.specified = AVTAB_XPERMS_ALLOWED; ebitmap_for_each_positive_bit(sattr, snode, i) { + tmp_key.source_type = i + 1; ebitmap_for_each_positive_bit(tattr, tnode, j) { - tmp_key.source_type = i + 1; tmp_key.target_type = j + 1; for (node = avtab_search_node(avtab, &tmp_key); node; @@ -168,40 +174,39 @@ static int report_assertion_extended_permissions(sepol_handle_t *handle, if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION) && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) continue; - + found_xperm = 1; rc = check_extended_permissions(avrule->xperms, xperms); /* failure on the extended permission check_extended_permissions */ if (rc) { extended_permissions_violated(&error, avrule->xperms, xperms); - ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of policy.conf) violated by\n" + ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of %s) violated by\n" "allowxperm %s %s:%s %s;", - avrule->source_line, avrule->source_filename, avrule->line, + avrule->source_line, avrule->source_filename, avrule->line, policy_name(p), p->p_type_val_to_name[i], p->p_type_val_to_name[j], p->p_class_val_to_name[curperm->tclass - 1], sepol_extended_perms_to_string(&error)); - rc = 0; - ret++; + errors++; } } } } /* failure on the regular permissions */ - if (rc) { - ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of policy.conf) violated by\n" + if (!found_xperm) { + ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of %s) violated by\n" "allow %s %s:%s {%s };", - avrule->source_line, avrule->source_filename, avrule->line, + avrule->source_line, avrule->source_filename, avrule->line, policy_name(p), p->p_type_val_to_name[stype], p->p_type_val_to_name[ttype], p->p_class_val_to_name[curperm->tclass - 1], sepol_av_to_string(p, curperm->tclass, perms)); - ret++; + errors++; } - return ret; + return errors; } static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void *args) @@ -214,9 +219,10 @@ static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void avrule_t *avrule = a->avrule; class_perm_node_t *cp; uint32_t perms; - ebitmap_t src_matches, tgt_matches, self_matches, matches; + ebitmap_t src_matches, tgt_matches, self_matches; ebitmap_node_t *snode, *tnode; unsigned int i, j; + const int is_avrule_self = (avrule->flags & RULE_SELF) != 0; if ((k->specified & AVTAB_ALLOWED) == 0) return 0; @@ -227,31 +233,27 @@ static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void ebitmap_init(&src_matches); ebitmap_init(&tgt_matches); ebitmap_init(&self_matches); - ebitmap_init(&matches); rc = ebitmap_and(&src_matches, &avrule->stypes.types, &p->attr_type_map[k->source_type - 1]); - if (rc) + if (rc < 0) goto oom; if (ebitmap_is_empty(&src_matches)) goto exit; rc = ebitmap_and(&tgt_matches, &avrule->ttypes.types, &p->attr_type_map[k->target_type -1]); - if (rc) + if (rc < 0) goto oom; - if (avrule->flags == RULE_SELF) { - rc = ebitmap_and(&matches, &p->attr_type_map[k->source_type - 1], &p->attr_type_map[k->target_type - 1]); - if (rc) - goto oom; - rc = ebitmap_and(&self_matches, &avrule->stypes.types, &matches); - if (rc) + if (is_avrule_self) { + rc = ebitmap_and(&self_matches, &src_matches, &p->attr_type_map[k->target_type - 1]); + if (rc < 0) goto oom; if (!ebitmap_is_empty(&self_matches)) { rc = ebitmap_union(&tgt_matches, &self_matches); - if (rc) + if (rc < 0) goto oom; } } @@ -268,6 +270,8 @@ static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void ebitmap_for_each_positive_bit(&src_matches, snode, i) { ebitmap_for_each_positive_bit(&tgt_matches, tnode, j) { + if (is_avrule_self && i != j) + continue; if (avrule->specified == AVRULE_XPERMS_NEVERALLOW) { a->errors += report_assertion_extended_permissions(handle,p, avrule, i, j, cp, perms, k, avtab); @@ -278,16 +282,12 @@ static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void } } } - goto exit; oom: - ERR(NULL, "Out of memory - unable to check neverallows"); - exit: ebitmap_destroy(&src_matches); ebitmap_destroy(&tgt_matches); ebitmap_destroy(&self_matches); - ebitmap_destroy(&matches); return rc; } @@ -301,12 +301,14 @@ static int report_assertion_failures(sepol_handle_t *handle, policydb_t *p, avru args.avrule = avrule; args.errors = 0; + args.avtab = &p->te_avtab; rc = avtab_map(&p->te_avtab, report_assertion_avtab_matches, &args); - if (rc) + if (rc < 0) goto oom; + args.avtab = &p->te_cond_avtab; rc = avtab_map(&p->te_cond_avtab, report_assertion_avtab_matches, &args); - if (rc) + if (rc < 0) goto oom; return args.errors; @@ -337,8 +339,8 @@ static int check_assertion_extended_permissions_avtab(avrule_t *avrule, avtab_t tmp_key.specified = AVTAB_XPERMS_ALLOWED; ebitmap_for_each_positive_bit(sattr, snode, i) { + tmp_key.source_type = i + 1; ebitmap_for_each_positive_bit(tattr, tnode, j) { - tmp_key.source_type = i + 1; tmp_key.target_type = j + 1; for (node = avtab_search_node(avtab, &tmp_key); node; @@ -350,7 +352,7 @@ static int check_assertion_extended_permissions_avtab(avrule_t *avrule, avtab_t continue; rc = check_extended_permissions(neverallow_xperms, xperms); if (rc) - break; + return rc; } } } @@ -377,125 +379,138 @@ static int check_assertion_extended_permissions_avtab(avrule_t *avrule, avtab_t static int check_assertion_extended_permissions(avrule_t *avrule, avtab_t *avtab, avtab_key_t *k, policydb_t *p) { - ebitmap_t src_matches, tgt_matches, self_matches, matches; + ebitmap_t src_matches, tgt_matches, self_matches; unsigned int i, j; ebitmap_node_t *snode, *tnode; - class_perm_node_t *cp; + const int is_avrule_self = (avrule->flags & RULE_SELF) != 0; int rc; - int ret = 1; ebitmap_init(&src_matches); ebitmap_init(&tgt_matches); ebitmap_init(&self_matches); - ebitmap_init(&matches); rc = ebitmap_and(&src_matches, &avrule->stypes.types, &p->attr_type_map[k->source_type - 1]); - if (rc) + if (rc < 0) goto oom; - if (ebitmap_is_empty(&src_matches)) + if (ebitmap_is_empty(&src_matches)) { + rc = 0; goto exit; + } rc = ebitmap_and(&tgt_matches, &avrule->ttypes.types, &p->attr_type_map[k->target_type -1]); - if (rc) + if (rc < 0) goto oom; - if (avrule->flags == RULE_SELF) { - rc = ebitmap_and(&matches, &p->attr_type_map[k->source_type - 1], - &p->attr_type_map[k->target_type - 1]); - if (rc) - goto oom; - rc = ebitmap_and(&self_matches, &avrule->stypes.types, &matches); - if (rc) + if (is_avrule_self) { + rc = ebitmap_and(&self_matches, &src_matches, &p->attr_type_map[k->target_type - 1]); + if (rc < 0) goto oom; if (!ebitmap_is_empty(&self_matches)) { rc = ebitmap_union(&tgt_matches, &self_matches); - if (rc) + if (rc < 0) goto oom; } } - if (ebitmap_is_empty(&tgt_matches)) + if (ebitmap_is_empty(&tgt_matches)) { + rc = 0; goto exit; + } - for (cp = avrule->perms; cp; cp = cp->next) { - if (cp->tclass != k->target_class) - continue; - ebitmap_for_each_positive_bit(&src_matches, snode, i) { - ebitmap_for_each_positive_bit(&tgt_matches, tnode, j) { - ret = check_assertion_extended_permissions_avtab( - avrule, avtab, i, j, k, p); - if (ret) - goto exit; + ebitmap_for_each_positive_bit(&src_matches, snode, i) { + ebitmap_for_each_positive_bit(&tgt_matches, tnode, j) { + if (is_avrule_self && i != j) + continue; + if (check_assertion_extended_permissions_avtab(avrule, avtab, i, j, k, p)) { + rc = 1; + goto exit; } } } - goto exit; -oom: - ERR(NULL, "Out of memory - unable to check neverallows"); + rc = 0; +oom: exit: ebitmap_destroy(&src_matches); ebitmap_destroy(&tgt_matches); - ebitmap_destroy(&matches); - return ret; + ebitmap_destroy(&self_matches); + return rc; +} + +static int check_assertion_self_match(avtab_key_t *k, avrule_t *avrule, policydb_t *p) +{ + ebitmap_t src_matches; + int rc; + + /* The key's target must match something in the matches of the avrule's source + * and the key's source. + */ + + rc = ebitmap_and(&src_matches, &avrule->stypes.types, &p->attr_type_map[k->source_type - 1]); + if (rc < 0) + goto oom; + + if (!ebitmap_match_any(&src_matches, &p->attr_type_map[k->target_type - 1])) { + rc = 0; + goto nomatch; + } + + rc = 1; + +oom: +nomatch: + ebitmap_destroy(&src_matches); + return rc; } static int check_assertion_avtab_match(avtab_key_t *k, avtab_datum_t *d, void *args) { - int rc, rc2 = 0; + int rc; struct avtab_match_args *a = (struct avtab_match_args *)args; policydb_t *p = a->p; avrule_t *avrule = a->avrule; avtab_t *avtab = a->avtab; if ((k->specified & AVTAB_ALLOWED) == 0) - goto exit; + goto nomatch; if (!match_any_class_permissions(avrule->perms, k->target_class, d->data)) - goto exit; + goto nomatch; - rc = ebitmap_match_any(&avrule->stypes.types, &p->attr_type_map[k->source_type - 1]); - if (rc == 0) - goto exit; + if (!ebitmap_match_any(&avrule->stypes.types, &p->attr_type_map[k->source_type - 1])) + goto nomatch; - if (avrule->flags == RULE_SELF) { - /* If the neverallow uses SELF, then it is not enough that the - * neverallow's source matches the src and tgt of the rule being checked. - * It must match the same thing in the src and tgt, so AND the source - * and target together and check for a match on the result. - */ - ebitmap_t match; - rc = ebitmap_and(&match, &p->attr_type_map[k->source_type - 1], &p->attr_type_map[k->target_type - 1] ); - if (rc) { - ebitmap_destroy(&match); - goto oom; + /* neverallow may have tgts even if it uses SELF */ + if (!ebitmap_match_any(&avrule->ttypes.types, &p->attr_type_map[k->target_type -1])) { + if (avrule->flags == RULE_SELF) { + rc = check_assertion_self_match(k, avrule, p); + if (rc < 0) + goto oom; + if (rc == 0) + goto nomatch; + } else { + goto nomatch; } - rc2 = ebitmap_match_any(&avrule->stypes.types, &match); - ebitmap_destroy(&match); } - /* neverallow may have tgts even if it uses SELF */ - rc = ebitmap_match_any(&avrule->ttypes.types, &p->attr_type_map[k->target_type -1]); - if (rc == 0 && rc2 == 0) - goto exit; - if (avrule->specified == AVRULE_XPERMS_NEVERALLOW) { rc = check_assertion_extended_permissions(avrule, avtab, k, p); + if (rc < 0) + goto oom; if (rc == 0) - goto exit; + goto nomatch; } return 1; -exit: +nomatch: return 0; oom: - ERR(NULL, "Out of memory - unable to check neverallows"); return rc; } @@ -538,6 +553,10 @@ int check_assertions(sepol_handle_t * handle, policydb_t * p, if (!(a->specified & (AVRULE_NEVERALLOW | AVRULE_XPERMS_NEVERALLOW))) continue; rc = check_assertion(p, a); + if (rc < 0) { + ERR(handle, "Error occurred while checking neverallows"); + return -1; + } if (rc) { rc = report_assertion_failures(handle, p, a); if (rc < 0) { diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c index 46e1e75d..7920b60a 100644 --- a/libsepol/src/avtab.c +++ b/libsepol/src/avtab.c @@ -503,6 +503,11 @@ int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a, for (i = 0; i < ARRAY_SIZE(spec_order); i++) { if (val & spec_order[i]) { + if (items >= items2) { /* items is index, items2 is total number */ + ERR(fp->handle, "entry has too many items (%d/%d)", + items + 1, items2); + return -1; + } key.specified = spec_order[i] | enabled; datum.data = le32_to_cpu(buf32[items++]); rc = insertf(a, &key, &datum, p); @@ -543,7 +548,7 @@ int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a, if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) && (key.specified & AVTAB_XPERMS)) { ERR(fp->handle, "policy version %u does not support extended " - "permissions rules and one was specified\n", vers); + "permissions rules and one was specified", vers); return -1; } else if (key.specified & AVTAB_XPERMS) { rc = next_entry(&buf8, fp, sizeof(uint8_t)); diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c index 037dc7e2..f78b38a2 100644 --- a/libsepol/src/conditional.c +++ b/libsepol/src/conditional.c @@ -25,6 +25,7 @@ #include <sepol/policydb/conditional.h> #include "private.h" +#include "debug.h" /* move all type rules to top of t/f lists to help kernel on evaluation */ static void cond_optimize(cond_av_list_t ** l) @@ -314,8 +315,7 @@ static int evaluate_cond_node(policydb_t * p, cond_node_t * node) if (new_state != node->cur_state) { node->cur_state = new_state; if (new_state == -1) - printf - ("expression result was undefined - disabling all rules.\n"); + WARN(NULL, "expression result was undefined - disabling all rules."); /* turn the rules on or off */ for (cur = node->true_list; cur != NULL; cur = cur->next) { if (new_state <= 0) { @@ -368,8 +368,7 @@ int cond_normalize_expr(policydb_t * p, cond_node_t * cn) if (ne) { ne->next = NULL; } else { /* ne should never be NULL */ - printf - ("Found expr with no bools and only a ! - this should never happen.\n"); + ERR(NULL, "Found expr with no bools and only a ! - this should never happen."); return -1; } /* swap the true and false lists */ @@ -421,9 +420,8 @@ int cond_normalize_expr(policydb_t * p, cond_node_t * cn) } k = cond_evaluate_expr(p, cn->expr); if (k == -1) { - printf - ("While testing expression, expression result " - "was undefined - this should never happen.\n"); + ERR(NULL, "While testing expression, expression result " + "was undefined - this should never happen."); return -1; } /* set the bit if expression evaluates true */ @@ -524,7 +522,7 @@ int cond_init_bool_indexes(policydb_t * p) if (p->bool_val_to_struct) free(p->bool_val_to_struct); p->bool_val_to_struct = (cond_bool_datum_t **) - malloc(p->p_bools.nprim * sizeof(cond_bool_datum_t *)); + mallocarray(p->p_bools.nprim, sizeof(cond_bool_datum_t *)); if (!p->bool_val_to_struct) return -1; return 0; @@ -635,9 +633,8 @@ static int cond_insertf(avtab_t * a */ if (k->specified & AVTAB_TYPE) { if (avtab_search(&p->te_avtab, k)) { - printf - ("security: type rule already exists outside of a conditional."); - goto err; + WARN(NULL, "security: type rule already exists outside of a conditional."); + return -1; } /* * If we are reading the false list other will be a pointer to @@ -652,9 +649,8 @@ static int cond_insertf(avtab_t * a if (node_ptr) { if (avtab_search_node_next (node_ptr, k->specified)) { - printf - ("security: too many conflicting type rules."); - goto err; + ERR(NULL, "security: too many conflicting type rules."); + return -1; } found = 0; for (cur = other; cur != NULL; cur = cur->next) { @@ -664,30 +660,28 @@ static int cond_insertf(avtab_t * a } } if (!found) { - printf - ("security: conflicting type rules.\n"); - goto err; + ERR(NULL, "security: conflicting type rules."); + return -1; } } } else { if (avtab_search(&p->te_cond_avtab, k)) { - printf - ("security: conflicting type rules when adding type rule for true.\n"); - goto err; + ERR(NULL, "security: conflicting type rules when adding type rule for true."); + return -1; } } } node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); if (!node_ptr) { - printf("security: could not insert rule."); - goto err; + ERR(NULL, "security: could not insert rule."); + return -1; } node_ptr->parse_context = (void *)1; list = malloc(sizeof(cond_av_list_t)); if (!list) - goto err; + return -1; memset(list, 0, sizeof(cond_av_list_t)); list->node = node_ptr; @@ -697,11 +691,6 @@ static int cond_insertf(avtab_t * a data->tail->next = list; data->tail = list; return 0; - - err: - cond_av_list_destroy(data->head); - data->head = NULL; - return -1; } static int cond_read_av_list(policydb_t * p, void *fp, @@ -730,8 +719,10 @@ static int cond_read_av_list(policydb_t * p, void *fp, for (i = 0; i < len; i++) { rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab, cond_insertf, &data); - if (rc) + if (rc) { + cond_av_list_destroy(data.head); return rc; + } } @@ -742,14 +733,12 @@ static int cond_read_av_list(policydb_t * p, void *fp, static int expr_isvalid(policydb_t * p, cond_expr_t * expr) { if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { - printf - ("security: conditional expressions uses unknown operator.\n"); + WARN(NULL, "security: conditional expressions uses unknown operator."); return 0; } if (expr->bool > p->p_bools.nprim) { - printf - ("security: conditional expressions uses unknown bool.\n"); + WARN(NULL, "security: conditional expressions uses unknown bool."); return 0; } return 1; diff --git a/libsepol/src/context_record.c b/libsepol/src/context_record.c index 435f7880..2bda121b 100644 --- a/libsepol/src/context_record.c +++ b/libsepol/src/context_record.c @@ -127,7 +127,7 @@ int sepol_context_create(sepol_handle_t * handle, sepol_context_t ** con_ptr) (sepol_context_t *) malloc(sizeof(sepol_context_t)); if (!con) { - ERR(handle, "out of memory, could not " "create context\n"); + ERR(handle, "out of memory, could not create context"); return STATUS_ERR; } diff --git a/libsepol/src/ebitmap.c b/libsepol/src/ebitmap.c index 1de3816a..bd98c0f8 100644 --- a/libsepol/src/ebitmap.c +++ b/libsepol/src/ebitmap.c @@ -406,8 +406,7 @@ int ebitmap_read(ebitmap_t * e, void *fp) count = le32_to_cpu(buf[2]); if (mapsize != MAPSIZE) { - printf - ("security: ebitmap: map size %d does not match my size %zu (high bit was %d)\n", + ERR(NULL, "security: ebitmap: map size %d does not match my size %zu (high bit was %d)", mapsize, MAPSIZE, e->highbit); goto bad; } @@ -416,8 +415,7 @@ int ebitmap_read(ebitmap_t * e, void *fp) goto ok; } if (e->highbit & (MAPSIZE - 1)) { - printf - ("security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)\n", + ERR(NULL, "security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)", e->highbit, MAPSIZE); goto bad; } @@ -429,12 +427,12 @@ int ebitmap_read(ebitmap_t * e, void *fp) for (i = 0; i < count; i++) { rc = next_entry(buf, fp, sizeof(uint32_t)); if (rc < 0) { - printf("security: ebitmap: truncated map\n"); + ERR(NULL, "security: ebitmap: truncated map"); goto bad; } n = (ebitmap_node_t *) malloc(sizeof(ebitmap_node_t)); if (!n) { - printf("security: ebitmap: out of memory\n"); + ERR(NULL, "security: ebitmap: out of memory"); rc = -ENOMEM; goto bad; } @@ -443,34 +441,30 @@ int ebitmap_read(ebitmap_t * e, void *fp) n->startbit = le32_to_cpu(buf[0]); if (n->startbit & (MAPSIZE - 1)) { - printf - ("security: ebitmap start bit (%d) is not a multiple of the map size (%zu)\n", + ERR(NULL, "security: ebitmap start bit (%d) is not a multiple of the map size (%zu)", n->startbit, MAPSIZE); goto bad_free; } if (n->startbit > (e->highbit - MAPSIZE)) { - printf - ("security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)\n", + ERR(NULL, "security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)", n->startbit, (e->highbit - MAPSIZE)); goto bad_free; } rc = next_entry(&map, fp, sizeof(uint64_t)); if (rc < 0) { - printf("security: ebitmap: truncated map\n"); + ERR(NULL, "security: ebitmap: truncated map"); goto bad_free; } n->map = le64_to_cpu(map); if (!n->map) { - printf - ("security: ebitmap: null map in ebitmap (startbit %d)\n", + ERR(NULL, "security: ebitmap: null map in ebitmap (startbit %d)", n->startbit); goto bad_free; } if (l) { if (n->startbit <= l->startbit) { - printf - ("security: ebitmap: start bit %d comes after start bit %d\n", + ERR(NULL, "security: ebitmap: start bit %d comes after start bit %d", n->startbit, l->startbit); goto bad_free; } @@ -481,8 +475,7 @@ int ebitmap_read(ebitmap_t * e, void *fp) l = n; } if (count && l->startbit + MAPSIZE != e->highbit) { - printf - ("security: ebitmap: high bit %u has not the expected value %zu\n", + ERR(NULL, "security: ebitmap: high bit %u has not the expected value %zu", e->highbit, l->startbit + MAPSIZE); goto bad; } diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c index a6a466f7..7da51a40 100644 --- a/libsepol/src/expand.c +++ b/libsepol/src/expand.c @@ -166,7 +166,7 @@ static int type_copy_callback(hashtab_key_t key, hashtab_datum_t datum, if (new_type->flags & TYPE_FLAGS_PERMISSIVE) if (ebitmap_set_bit(&state->out->permissive_map, new_type->s.value, 1)) { - ERR(state->handle, "Out of memory!\n"); + ERR(state->handle, "Out of memory!"); return -1; } @@ -929,11 +929,15 @@ int mls_semantic_level_expand(mls_semantic_level_t * sl, mls_level_t * l, if (!sl->sens) return 0; + /* Invalid sensitivity */ + if (sl->sens > p->p_levels.nprim || !p->p_sens_val_to_name[sl->sens - 1]) + return -1; + l->sens = sl->sens; levdatum = (level_datum_t *) hashtab_search(p->p_levels.table, p->p_sens_val_to_name[l->sens - 1]); if (!levdatum) { - ERR(h, "%s: Impossible situation found, nothing in p_levels.table.\n", + ERR(h, "%s: Impossible situation found, nothing in p_levels.table.", __func__); errno = ENOENT; return -1; @@ -1690,7 +1694,7 @@ static int expand_terule_helper(sepol_handle_t * handle, uint32_t oldtype = 0; if (!(specified & (AVRULE_TRANSITION|AVRULE_MEMBER|AVRULE_CHANGE))) { - ERR(handle, "Invalid specification: %"PRIu32"\n", specified); + ERR(handle, "Invalid specification: %"PRIu32, specified); return EXPAND_RULE_ERROR; } @@ -1869,7 +1873,7 @@ static int expand_avrule_helper(sepol_handle_t * handle, return EXPAND_RULE_ERROR; break; default: - ERR(handle, "Unknown specification: %"PRIu32"\n", specified); + ERR(handle, "Unknown specification: %"PRIu32, specified); return EXPAND_RULE_ERROR; } @@ -2477,7 +2481,7 @@ int role_set_expand(role_set_t * x, ebitmap_t * r, policydb_t * out, policydb_t /* if role is to be complimented, invert the entire bitmap here */ if (x->flags & ROLE_COMP) { - for (i = 0; i < ebitmap_length(r); i++) { + for (i = 0; i < p->p_roles.nprim; i++) { if (ebitmap_get_bit(r, i)) { if (ebitmap_set_bit(r, i, 0)) return -1; @@ -2966,6 +2970,9 @@ int expand_module(sepol_handle_t * handle, state.out->policy_type = POLICY_KERN; state.out->policyvers = POLICYDB_VERSION_MAX; + if (state.base->name) { + state.out->name = strdup(state.base->name); + } /* Copy mls state from base to out */ out->mls = base->mls; @@ -3146,9 +3153,9 @@ int expand_module(sepol_handle_t * handle, goto cleanup; /* Build the type<->attribute maps and remove attributes. */ - state.out->attr_type_map = malloc(state.out->p_types.nprim * + state.out->attr_type_map = mallocarray(state.out->p_types.nprim, sizeof(ebitmap_t)); - state.out->type_attr_map = malloc(state.out->p_types.nprim * + state.out->type_attr_map = mallocarray(state.out->p_types.nprim, sizeof(ebitmap_t)); if (!state.out->attr_type_map || !state.out->type_attr_map) { ERR(handle, "Out of memory!"); diff --git a/libsepol/src/hashtab.c b/libsepol/src/hashtab.c index 21143b76..3ecaf165 100644 --- a/libsepol/src/hashtab.c +++ b/libsepol/src/hashtab.c @@ -32,6 +32,8 @@ #include <string.h> #include <sepol/policydb/hashtab.h> +#include "private.h" + hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h, const_hashtab_key_t key), int (*keycmp) (hashtab_t h, @@ -52,7 +54,7 @@ hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h, p->nel = 0; p->hash_value = hash_value; p->keycmp = keycmp; - p->htable = (hashtab_ptr_t *) malloc(sizeof(hashtab_ptr_t) * size); + p->htable = (hashtab_ptr_t *) mallocarray(size, sizeof(hashtab_ptr_t)); if (p->htable == NULL) { free(p); return NULL; @@ -222,8 +224,9 @@ int hashtab_map(hashtab_t h, int (*apply) (hashtab_key_t k, hashtab_datum_t d, void *args), void *args) { - unsigned int i, ret; + unsigned int i; hashtab_ptr_t cur; + int ret; if (!h) return SEPOL_OK; diff --git a/libsepol/src/hierarchy.c b/libsepol/src/hierarchy.c index 8919daa7..350443a8 100644 --- a/libsepol/src/hierarchy.c +++ b/libsepol/src/hierarchy.c @@ -237,7 +237,7 @@ oom: ERR(handle, "Insufficient memory"); exit: - ERR(handle,"Failed to expand parent rules\n"); + ERR(handle,"Failed to expand parent rules"); avtab_destroy(global_avtab); bounds_destroy_cond_info(*cond_info); *cond_info = NULL; diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c index 305567a5..869f6940 100644 --- a/libsepol/src/kernel_to_cil.c +++ b/libsepol/src/kernel_to_cil.c @@ -278,10 +278,13 @@ static int class_constraint_rules_to_strs(struct policydb *pdb, char *classkey, char *expr = NULL; int is_mls; char *perms; - const char *format_str; + const char *key_word; struct strs *strs; for (curr = constraint_rules; curr != NULL; curr = curr->next) { + if (curr->permissions == 0) { + continue; + } expr = constraint_expr_to_str(pdb, curr->expr, &is_mls); if (!expr) { rc = -1; @@ -291,14 +294,14 @@ static int class_constraint_rules_to_strs(struct policydb *pdb, char *classkey, perms = sepol_av_to_string(pdb, class->s.value, curr->permissions); if (is_mls) { - format_str = "(mlsconstrain (%s (%s)) %s)"; + key_word = "mlsconstrain"; strs = mls_list; } else { - format_str = "(constrain (%s (%s)) %s)"; + key_word = "constrain"; strs = non_mls_list; } - rc = strs_create_and_add(strs, format_str, 3, classkey, perms+1, expr); + rc = strs_create_and_add(strs, "(%s (%s (%s)) %s)", 4, key_word, classkey, perms+1, expr); free(expr); if (rc != 0) { goto exit; @@ -319,7 +322,7 @@ static int class_validatetrans_rules_to_strs(struct policydb *pdb, char *classke struct constraint_node *curr; char *expr = NULL; int is_mls; - const char *format_str; + const char *key_word; struct strs *strs; int rc = 0; @@ -331,14 +334,14 @@ static int class_validatetrans_rules_to_strs(struct policydb *pdb, char *classke } if (is_mls) { - format_str = "(mlsvalidatetrans %s %s)"; + key_word = "mlsvalidatetrans"; strs = mls_list; } else { - format_str = "(validatetrans %s %s)"; + key_word = "validatetrans"; strs = non_mls_list; } - rc = strs_create_and_add(strs, format_str, 2, classkey, expr); + rc = strs_create_and_add(strs, "(%s %s %s)", 3, key_word, classkey, expr); free(expr); if (rc != 0) { goto exit; @@ -358,6 +361,7 @@ static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs, for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->constraints) { name = pdb->p_class_val_to_name[i]; rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs); @@ -383,6 +387,7 @@ static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_st for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->validatetrans) { name = pdb->p_class_val_to_name[i]; rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs); @@ -461,6 +466,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb) /* class */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; name = pdb->p_class_val_to_name[i]; perms = class_or_common_perms_to_str(&class->permissions); if (perms) { @@ -488,6 +494,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb) /* classcommon */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; name = pdb->p_class_val_to_name[i]; if (class->comkey != NULL) { sepol_printf(out, "(classcommon %s %s)\n", name, class->comkey); @@ -503,6 +510,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb) } for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; name = class->comkey; if (name != NULL) { common = hashtab_search(pdb->p_commons.table, name); @@ -727,6 +735,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb) /* default_user */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->default_user != 0) { rc = write_default_user_to_cil(out, pdb->p_class_val_to_name[i], class); if (rc != 0) { @@ -738,6 +747,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb) /* default_role */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->default_role != 0) { rc = write_default_role_to_cil(out, pdb->p_class_val_to_name[i], class); if (rc != 0) { @@ -749,6 +759,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb) /* default_type */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->default_type != 0) { rc = write_default_type_to_cil(out, pdb->p_class_val_to_name[i], class); if (rc != 0) { @@ -764,6 +775,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb) /* default_range */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->default_range) { rc = write_default_range_to_cil(out, pdb->p_class_val_to_name[i], class); if (rc != 0) { @@ -1035,7 +1047,6 @@ static char *cats_ebitmap_to_str(struct ebitmap *cats, char **val_to_name) struct ebitmap_node *node; uint32_t i, start, range; char *catsbuf = NULL, *p; - const char *fmt; int len, remaining; remaining = (int)cats_ebitmap_len(cats, val_to_name); @@ -1063,9 +1074,15 @@ static char *cats_ebitmap_to_str(struct ebitmap *cats, char **val_to_name) continue; if (range > 1) { - fmt = (range == 2) ? "%s %s " : "(range %s %s) "; - len = snprintf(p, remaining, fmt, - val_to_name[start], val_to_name[i]); + if (range == 2) { + len = snprintf(p, remaining, "%s %s ", + val_to_name[start], + val_to_name[i]); + } else { + len = snprintf(p, remaining, "(range %s %s) ", + val_to_name[start], + val_to_name[i]); + } } else { len = snprintf(p, remaining, "%s ", val_to_name[start]); } @@ -1213,7 +1230,7 @@ static int write_type_attributes_to_cil(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { type = pdb->type_val_to_struct[i]; - if (type->flavor == TYPE_ATTRIB) { + if (type && type->flavor == TYPE_ATTRIB) { rc = strs_add(strs, pdb->p_type_val_to_name[i]); if (rc != 0) { goto exit; @@ -1343,7 +1360,7 @@ static int write_type_decl_rules_to_cil(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { type = pdb->type_val_to_struct[i]; - if (type->flavor == TYPE_TYPE && type->primary) { + if (type && type->flavor == TYPE_TYPE && type->primary) { rc = strs_add(strs, pdb->p_type_val_to_name[i]); if (rc != 0) { goto exit; @@ -1472,7 +1489,7 @@ static int write_type_bounds_rules_to_cil(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { type = pdb->type_val_to_struct[i]; - if (type->flavor == TYPE_TYPE) { + if (type && type->flavor == TYPE_TYPE) { if (type->bounds > 0) { rc = strs_add(strs, pdb->p_type_val_to_name[i]); if (rc != 0) { @@ -1526,7 +1543,7 @@ static int write_type_attribute_sets_to_cil(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { attr = pdb->type_val_to_struct[i]; - if (attr->flavor != TYPE_ATTRIB) continue; + if (!attr || attr->flavor != TYPE_ATTRIB) continue; name = pdb->p_type_val_to_name[i]; typemap = &pdb->attr_type_map[i]; if (ebitmap_is_empty(typemap)) continue; @@ -2259,7 +2276,7 @@ static int write_role_decl_rules_to_cil(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { type_datum = pdb->type_val_to_struct[i]; - if (type_datum->flavor == TYPE_TYPE && type_datum->primary) { + if (type_datum && type_datum->flavor == TYPE_TYPE && type_datum->primary) { rc = strs_add(strs, pdb->p_type_val_to_name[i]); if (rc != 0) { goto exit; @@ -2383,6 +2400,7 @@ static int write_user_decl_rules_to_cil(FILE *out, struct policydb *pdb) } for (i=0; i < pdb->p_users.nprim; i++) { + if (!pdb->p_user_val_to_name[i]) continue; rc = strs_add(strs, pdb->p_user_val_to_name[i]); if (rc != 0) { goto exit; @@ -2640,6 +2658,8 @@ static int write_genfscon_rules_to_cil(FILE *out, struct policydb *pdb) struct ocontext *ocon; struct strs *strs; char *fstype, *name, *ctx; + uint32_t sclass; + const char *file_type; int rc; rc = strs_init(&strs, 32); @@ -2652,14 +2672,43 @@ static int write_genfscon_rules_to_cil(FILE *out, struct policydb *pdb) fstype = genfs->fstype; name = ocon->u.name; + sclass = ocon->v.sclass; + file_type = NULL; + if (sclass) { + const char *class_name = pdb->p_class_val_to_name[sclass-1]; + if (strcmp(class_name, "file") == 0) { + file_type = "file"; + } else if (strcmp(class_name, "dir") == 0) { + file_type = "dir"; + } else if (strcmp(class_name, "chr_file") == 0) { + file_type = "char"; + } else if (strcmp(class_name, "blk_file") == 0) { + file_type = "block"; + } else if (strcmp(class_name, "sock_file") == 0) { + file_type = "socket"; + } else if (strcmp(class_name, "fifo_file") == 0) { + file_type = "pipe"; + } else if (strcmp(class_name, "lnk_file") == 0) { + file_type = "symlink"; + } else { + rc = -1; + goto exit; + } + } + ctx = context_to_str(pdb, &ocon->context[0]); if (!ctx) { rc = -1; goto exit; } - rc = strs_create_and_add(strs, "(genfscon %s \"%s\" %s)", 3, - fstype, name, ctx); + if (file_type) { + rc = strs_create_and_add(strs, "(genfscon %s \"%s\" %s %s)", 4, + fstype, name, file_type, ctx); + } else { + rc = strs_create_and_add(strs, "(genfscon %s \"%s\" %s)", 3, + fstype, name, ctx); + } free(ctx); if (rc != 0) { goto exit; diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c index a7453d3c..972499ab 100644 --- a/libsepol/src/kernel_to_common.c +++ b/libsepol/src/kernel_to_common.c @@ -18,6 +18,7 @@ #include <sepol/policydb/hashtab.h> #include <sepol/policydb/symtab.h> +#include "private.h" #include "kernel_to_common.h" @@ -57,7 +58,7 @@ static char *create_str_helper(const char *fmt, int num, va_list vargs) va_list vargs2; char *str = NULL; char *s; - size_t len; + size_t len, s_len; int i, rc; va_copy(vargs2, vargs); @@ -66,7 +67,8 @@ static char *create_str_helper(const char *fmt, int num, va_list vargs) for (i=0; i<num; i++) { s = va_arg(vargs, char *); - len += strlen(s) - 2; /* -2 for each %s in fmt */ + s_len = strlen(s); + len += s_len > 1 ? s_len - 2 : 0; /* -2 for each %s in fmt */ } str = malloc(len); @@ -106,6 +108,10 @@ int strs_init(struct strs **strs, size_t size) { struct strs *new; + if (size == 0) { + size = 1; + } + *strs = NULL; new = malloc(sizeof(struct strs)); @@ -114,7 +120,7 @@ int strs_init(struct strs **strs, size_t size) return -1; } - new->list = calloc(sizeof(char *), size); + new->list = calloc(size, sizeof(char *)); if (!new->list) { sepol_log_err("Out of memory"); free(new); @@ -159,9 +165,9 @@ int strs_add(struct strs *strs, char *s) { if (strs->num + 1 > strs->size) { char **new; - unsigned i = strs->size; + size_t i = strs->size; strs->size *= 2; - new = realloc(strs->list, sizeof(char *)*strs->size); + new = reallocarray(strs->list, strs->size, sizeof(char *)); if (!new) { sepol_log_err("Out of memory"); return -1; @@ -212,15 +218,15 @@ char *strs_remove_last(struct strs *strs) return strs->list[strs->num]; } -int strs_add_at_index(struct strs *strs, char *s, unsigned index) +int strs_add_at_index(struct strs *strs, char *s, size_t index) { if (index >= strs->size) { char **new; - unsigned i = strs->size; + size_t i = strs->size; while (index >= strs->size) { strs->size *= 2; } - new = realloc(strs->list, sizeof(char *)*strs->size); + new = reallocarray(strs->list, strs->size, sizeof(char *)); if (!new) { sepol_log_err("Out of memory"); return -1; @@ -237,7 +243,7 @@ int strs_add_at_index(struct strs *strs, char *s, unsigned index) return 0; } -char *strs_read_at_index(struct strs *strs, unsigned index) +char *strs_read_at_index(struct strs *strs, size_t index) { if (index >= strs->num) { return NULL; @@ -361,6 +367,9 @@ int ebitmap_to_strs(struct ebitmap *map, struct strs *strs, char **val_to_name) int rc; ebitmap_for_each_positive_bit(map, node, i) { + if (!val_to_name[i]) + continue; + rc = strs_add(strs, val_to_name[i]); if (rc != 0) { return -1; diff --git a/libsepol/src/kernel_to_common.h b/libsepol/src/kernel_to_common.h index 8aa483fa..e9932d30 100644 --- a/libsepol/src/kernel_to_common.h +++ b/libsepol/src/kernel_to_common.h @@ -99,8 +99,8 @@ int strs_add(struct strs *strs, char *s); __attribute__ ((format(printf, 2, 4))) int strs_create_and_add(struct strs *strs, const char *fmt, int num, ...); char *strs_remove_last(struct strs *strs); -int strs_add_at_index(struct strs *strs, char *s, unsigned index); -char *strs_read_at_index(struct strs *strs, unsigned index); +int strs_add_at_index(struct strs *strs, char *s, size_t index); +char *strs_read_at_index(struct strs *strs, size_t index); void strs_sort(struct strs *strs); unsigned strs_num_items(struct strs *strs); size_t strs_len_items(struct strs *strs); diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c index eb72e4ac..3544f73d 100644 --- a/libsepol/src/kernel_to_conf.c +++ b/libsepol/src/kernel_to_conf.c @@ -271,12 +271,15 @@ static int class_constraint_rules_to_strs(struct policydb *pdb, char *classkey, { struct constraint_node *curr; struct strs *strs; - const char *format_str, *flavor; + const char *flavor, *perm_prefix, *perm_suffix; char *perms, *expr; int is_mls; int rc = 0; for (curr = constraint_rules; curr != NULL; curr = curr->next) { + if (curr->permissions == 0) { + continue; + } expr = constraint_expr_to_str(pdb, curr->expr, &is_mls); if (!expr) { rc = -1; @@ -285,9 +288,11 @@ static int class_constraint_rules_to_strs(struct policydb *pdb, char *classkey, perms = sepol_av_to_string(pdb, class->s.value, curr->permissions); if (strchr(perms, ' ')) { - format_str = "%s %s { %s } %s;"; + perm_prefix = "{ "; + perm_suffix = " }"; } else { - format_str = "%s %s %s %s"; + perm_prefix = ""; + perm_suffix = ""; } if (is_mls) { flavor = "mlsconstrain"; @@ -297,8 +302,10 @@ static int class_constraint_rules_to_strs(struct policydb *pdb, char *classkey, strs = non_mls_list; } - rc = strs_create_and_add(strs, format_str, 4, - flavor, classkey, perms+1, expr); + rc = strs_create_and_add(strs, "%s %s %s%s%s %s;", 6, + flavor, classkey, + perm_prefix, perms+1, perm_suffix, + expr); free(expr); if (rc != 0) { goto exit; @@ -358,7 +365,7 @@ static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs, for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; - if (class->constraints) { + if (class && class->constraints) { name = pdb->p_class_val_to_name[i]; rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs); if (rc != 0) { @@ -383,7 +390,7 @@ static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_st for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; - if (class->validatetrans) { + if (class && class->validatetrans) { name = pdb->p_class_val_to_name[i]; rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs); if (rc != 0) { @@ -551,6 +558,7 @@ static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb) } for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; name = class->comkey; if (!name) continue; common = hashtab_search(pdb->p_commons.table, name); @@ -577,6 +585,7 @@ static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb) /* class */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; name = pdb->p_class_val_to_name[i]; sepol_printf(out, "class %s", name); if (class->comkey) { @@ -702,6 +711,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb) /* default_user */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->default_user != 0) { rc = write_default_user_to_conf(out, pdb->p_class_val_to_name[i], class); if (rc != 0) { @@ -713,6 +723,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb) /* default_role */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->default_role != 0) { rc = write_default_role_to_conf(out, pdb->p_class_val_to_name[i], class); if (rc != 0) { @@ -724,6 +735,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb) /* default_type */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->default_type != 0) { rc = write_default_type_to_conf(out, pdb->p_class_val_to_name[i], class); if (rc != 0) { @@ -739,6 +751,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb) /* default_range */ for (i=0; i < pdb->p_classes.nprim; i++) { class = pdb->class_val_to_struct[i]; + if (!class) continue; if (class->default_range != 0) { rc = write_default_range_to_conf(out, pdb->p_class_val_to_name[i], class); if (rc != 0) { @@ -908,7 +921,7 @@ static int write_category_rules_to_conf(FILE *out, struct policydb *pdb) unsigned i, j, num; int rc = 0; - rc = strs_init(&strs, pdb->p_levels.nprim); + rc = strs_init(&strs, pdb->p_cats.nprim); if (rc != 0) { goto exit; } @@ -1026,7 +1039,6 @@ static char *cats_ebitmap_to_str(struct ebitmap *cats, char **val_to_name) struct ebitmap_node *node; uint32_t i, start, range, first; char *catsbuf = NULL, *p; - const char *fmt; char sep; int len, remaining; @@ -1054,12 +1066,12 @@ static char *cats_ebitmap_to_str(struct ebitmap *cats, char **val_to_name) if (range > 1) { sep = (range == 2) ? ',' : '.'; - fmt = first ? "%s%c%s" : ",%s%c%s"; - len = snprintf(p, remaining, fmt, + len = snprintf(p, remaining, "%s%s%c%s", + first ? "" : ",", val_to_name[start], sep, val_to_name[i]); } else { - fmt = first ? "%s" : ",%s"; - len = snprintf(p, remaining, fmt, val_to_name[start]); + len = snprintf(p, remaining, "%s%s", first ? "" : ",", + val_to_name[start]); } if (len < 0 || len >= remaining) { @@ -1201,7 +1213,7 @@ static int write_type_attributes_to_conf(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { type = pdb->type_val_to_struct[i]; - if (type->flavor == TYPE_ATTRIB) { + if (type && type->flavor == TYPE_ATTRIB) { rc = strs_add(strs, pdb->p_type_val_to_name[i]); if (rc != 0) { goto exit; @@ -1331,7 +1343,7 @@ static int write_type_decl_rules_to_conf(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { type = pdb->type_val_to_struct[i]; - if (type->flavor == TYPE_TYPE && type->primary) { + if (type && type->flavor == TYPE_TYPE && type->primary) { rc = strs_add(strs, pdb->p_type_val_to_name[i]); if (rc != 0) { goto exit; @@ -1451,7 +1463,7 @@ static int write_type_bounds_rules_to_conf(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { type = pdb->type_val_to_struct[i]; - if (type->flavor == TYPE_TYPE) { + if (type && type->flavor == TYPE_TYPE) { if (type->bounds > 0) { rc = strs_add(strs, pdb->p_type_val_to_name[i]); if (rc != 0) { @@ -1574,7 +1586,7 @@ static int write_type_attribute_sets_to_conf(FILE *out, struct policydb *pdb) for (i=0; i < pdb->p_types.nprim; i++) { type = pdb->type_val_to_struct[i]; - if (type->flavor != TYPE_TYPE || !type->primary) continue; + if (!type || type->flavor != TYPE_TYPE || !type->primary) continue; if (ebitmap_cardinality(&pdb->type_attr_map[i]) == 1) continue; rc = ebitmap_cpy(&attrmap, &pdb->type_attr_map[i]); @@ -2318,6 +2330,7 @@ static int write_user_decl_rules_to_conf(FILE *out, struct policydb *pdb) } for (i=0; i < pdb->p_users.nprim; i++) { + if (!pdb->p_user_val_to_name[i]) continue; rc = strs_add(strs, pdb->p_user_val_to_name[i]); if (rc != 0) { goto exit; @@ -2513,6 +2526,8 @@ static int write_genfscon_rules_to_conf(FILE *out, struct policydb *pdb) struct ocontext *ocon; struct strs *strs; char *fstype, *name, *ctx; + uint32_t sclass; + const char *file_type; int rc; rc = strs_init(&strs, 32); @@ -2525,14 +2540,43 @@ static int write_genfscon_rules_to_conf(FILE *out, struct policydb *pdb) fstype = genfs->fstype; name = ocon->u.name; + sclass = ocon->v.sclass; + file_type = NULL; + if (sclass) { + const char *class_name = pdb->p_class_val_to_name[sclass-1]; + if (strcmp(class_name, "file") == 0) { + file_type = "--"; + } else if (strcmp(class_name, "dir") == 0) { + file_type = "-d"; + } else if (strcmp(class_name, "chr_file") == 0) { + file_type = "-c"; + } else if (strcmp(class_name, "blk_file") == 0) { + file_type = "-b"; + } else if (strcmp(class_name, "sock_file") == 0) { + file_type = "-s"; + } else if (strcmp(class_name, "fifo_file") == 0) { + file_type = "-p"; + } else if (strcmp(class_name, "lnk_file") == 0) { + file_type = "-l"; + } else { + rc = -1; + goto exit; + } + } + ctx = context_to_str(pdb, &ocon->context[0]); if (!ctx) { rc = -1; goto exit; } - rc = strs_create_and_add(strs, "genfscon %s \"%s\" %s", 3, - fstype, name, ctx); + if (file_type) { + rc = strs_create_and_add(strs, "genfscon %s \"%s\" %s %s", 4, + fstype, name, file_type, ctx); + } else { + rc = strs_create_and_add(strs, "genfscon %s \"%s\" %s", 3, + fstype, name, ctx); + } free(ctx); if (rc != 0) { goto exit; diff --git a/libsepol/src/link.c b/libsepol/src/link.c index 7512a4d9..21a5a935 100644 --- a/libsepol/src/link.c +++ b/libsepol/src/link.c @@ -34,6 +34,7 @@ #include <assert.h> #include "debug.h" +#include "private.h" #undef min #define min(a,b) (((a) < (b)) ? (a) : (b)) @@ -164,7 +165,7 @@ static int permission_copy_callback(hashtab_key_t key, hashtab_datum_t datum, (hashtab_datum_t) new_perm); if (ret) { ERR(state->handle, - "could not insert permission into class\n"); + "could not insert permission into class"); goto err; } new_perm->s.value = dest_class->permissions.nprim + 1; @@ -190,8 +191,9 @@ static int permission_copy_callback(hashtab_key_t key, hashtab_datum_t datum, ERR(state->handle, "Out of memory!"); return -1; } - memcpy(newmap, mod->perm_map[sclassi], - mod->perm_map_len[sclassi] * sizeof(*newmap)); + if (mod->perm_map_len[sclassi] > 0) { + memcpy(newmap, mod->perm_map[sclassi], mod->perm_map_len[sclassi] * sizeof(*newmap)); + } free(mod->perm_map[sclassi]); mod->perm_map[sclassi] = newmap; mod->perm_map_len[sclassi] = perm->s.value; @@ -287,7 +289,7 @@ static int class_copy_callback(hashtab_key_t key, hashtab_datum_t datum, new_class = (class_datum_t *) calloc(1, sizeof(class_datum_t)); if (new_class == NULL) { - ERR(state->handle, "Memory error\n"); + ERR(state->handle, "Memory error"); ret = SEPOL_ERR; goto err; } @@ -298,7 +300,7 @@ static int class_copy_callback(hashtab_key_t key, hashtab_datum_t datum, } new_id = strdup(id); if (new_id == NULL) { - ERR(state->handle, "Memory error\n"); + ERR(state->handle, "Memory error"); symtab_destroy(&new_class->permissions); ret = SEPOL_ERR; goto err; @@ -694,7 +696,7 @@ static int sens_copy_callback(hashtab_key_t key, hashtab_datum_t datum, return SEPOL_ENOTSUP; } else { ERR(state->handle, - "%s: has an unknown scope: %d\n", + "%s: has an unknown scope: %d", state->cur_mod_name, scope->scope); return SEPOL_ENOTSUP; } @@ -736,7 +738,7 @@ static int cat_copy_callback(hashtab_key_t key, hashtab_datum_t datum, } else { /* unknown scope? malformed policy? */ ERR(state->handle, - "%s: has an unknown scope: %d\n", + "%s: has an unknown scope: %d", state->cur_mod_name, scope->scope); return SEPOL_ENOTSUP; } @@ -1679,7 +1681,7 @@ static int copy_scope_index(scope_index_t * src, scope_index_t * dest, } /* next copy the enabled permissions data */ - if ((dest->class_perms_map = malloc(largest_mapped_class_value * + if ((dest->class_perms_map = mallocarray(largest_mapped_class_value, sizeof(*dest->class_perms_map))) == NULL) { goto cleanup; @@ -1779,7 +1781,7 @@ static int copy_avrule_block(link_state_t * state, policy_module_t * module, if (module->policy->name != NULL) { new_decl->module_name = strdup(module->policy->name); if (new_decl->module_name == NULL) { - ERR(state->handle, "Out of memory\n"); + ERR(state->handle, "Out of memory"); avrule_decl_destroy(new_decl); ret = -1; goto cleanup; @@ -2206,7 +2208,7 @@ static int enable_avrules(link_state_t * state, policydb_t * pol) if (state->verbose) { const char *mod_name = decl->module_name ? decl->module_name : "BASE"; - INFO(state->handle, "check module %s decl %d\n", + INFO(state->handle, "check module %s decl %d", mod_name, decl->decl_id); } rc = is_decl_requires_met(state, decl, &req); @@ -2552,7 +2554,7 @@ int link_modules(sepol_handle_t * handle, if (mods[i]->policyvers > b->policyvers) { WARN(state.handle, - "Upgrading policy version from %u to %u\n", b->policyvers, mods[i]->policyvers); + "Upgrading policy version from %u to %u", b->policyvers, mods[i]->policyvers); b->policyvers = mods[i]->policyvers; } diff --git a/libsepol/src/module.c b/libsepol/src/module.c index 02a5de2c..d93d08a2 100644 --- a/libsepol/src/module.c +++ b/libsepol/src/module.c @@ -293,11 +293,14 @@ static int link_netfilter_contexts(sepol_module_package_t * base, } base->netfilter_contexts = base_context; for (i = 0; i < num_modules; i++) { - memcpy(base->netfilter_contexts + base->netfilter_contexts_len, - modules[i]->netfilter_contexts, - modules[i]->netfilter_contexts_len); - base->netfilter_contexts_len += - modules[i]->netfilter_contexts_len; + if (modules[i]->netfilter_contexts_len > 0) { + memcpy(base->netfilter_contexts + base->netfilter_contexts_len, + modules[i]->netfilter_contexts, + modules[i]->netfilter_contexts_len); + base->netfilter_contexts_len += + modules[i]->netfilter_contexts_len; + } + } return 0; } @@ -406,14 +409,14 @@ static int module_package_read_offsets(sepol_module_package_t * mod, goto err; } - off = (size_t *) malloc((nsec + 1) * sizeof(size_t)); + off = (size_t *) mallocarray(nsec + 1, sizeof(size_t)); if (!off) { ERR(file->handle, "out of memory"); goto err; } free(buf); - buf = malloc(sizeof(uint32_t) * nsec); + buf = mallocarray(nsec, sizeof(uint32_t)); if (!buf) { ERR(file->handle, "out of memory"); goto err; diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c index 16e4004e..c9e88f1e 100644 --- a/libsepol/src/module_to_cil.c +++ b/libsepol/src/module_to_cil.c @@ -430,7 +430,7 @@ static int stack_init(struct stack **stack) goto exit; } - s->stack = malloc(sizeof(*s->stack) * STACK_SIZE); + s->stack = mallocarray(STACK_SIZE, sizeof(*s->stack)); if (s->stack == NULL) { goto exit; } @@ -453,7 +453,7 @@ static int stack_push(struct stack *stack, void *ptr) void *new_stack; if (stack->pos + 1 == stack->size) { - new_stack = realloc(stack->stack, sizeof(*stack->stack) * (stack->size * 2)); + new_stack = reallocarray(stack->stack, stack->size * 2, sizeof(*stack->stack)); if (new_stack == NULL) { goto exit; } @@ -1008,7 +1008,7 @@ static int ebitmap_to_names(struct ebitmap *map, char **vals_to_names, char ***n goto exit; } - name_arr = malloc(sizeof(*name_arr) * num); + name_arr = mallocarray(num, sizeof(*name_arr)); if (name_arr == NULL) { log_err("Out of memory"); rc = -1; @@ -1259,7 +1259,7 @@ static int cond_expr_to_cil(int indent, struct policydb *pdb, struct cond_expr * char *val2 = NULL; unsigned int num_params; const char *op; - const char *fmt_str; + const char *sep; const char *type; rc = stack_init(&stack); @@ -1308,11 +1308,11 @@ static int cond_expr_to_cil(int indent, struct policydb *pdb, struct cond_expr * rc = -1; goto exit; } - fmt_str = "(%s %s)"; + sep = ""; } else { val2 = stack_pop(stack); val1 = stack_pop(stack); - fmt_str = "(%s %s %s)"; + sep = " "; } if (val1 == NULL || val2 == NULL) { @@ -1334,10 +1334,7 @@ static int cond_expr_to_cil(int indent, struct policydb *pdb, struct cond_expr * goto exit; } - // although we always supply val2 and there isn't always a 2nd - // value, it should only be used when there are actually two values - // in the format strings - rlen = snprintf(new_val, len, fmt_str, op, val1, val2); + rlen = snprintf(new_val, len, "(%s %s%s%s)", op, val1, sep, val2); if (rlen < 0 || rlen >= len) { log_err("Failed to generate conditional expression"); rc = -1; @@ -1711,7 +1708,7 @@ static int constraint_expr_to_string(struct policydb *pdb, struct constraint_exp char *val2 = NULL; uint32_t num_params; const char *op; - const char *fmt_str; + const char *sep; const char *attr1; const char *attr2; char *names = NULL; @@ -1849,11 +1846,11 @@ static int constraint_expr_to_string(struct policydb *pdb, struct constraint_exp rc = -1; goto exit; } - fmt_str = "(%s %s)"; + sep = ""; } else { val2 = stack_pop(stack); val1 = stack_pop(stack); - fmt_str = "(%s %s %s)"; + sep = " "; } if (val1 == NULL || val2 == NULL) { @@ -1875,10 +1872,7 @@ static int constraint_expr_to_string(struct policydb *pdb, struct constraint_exp goto exit; } - // although we always supply val2 and there isn't always a 2nd - // value, it should only be used when there are actually two values - // in the format strings - rlen = snprintf(new_val, len, fmt_str, op, val1, val2); + rlen = snprintf(new_val, len, "(%s %s%s%s)", op, val1, sep, val2); if (rlen < 0 || rlen >= len) { log_err("Failed to generate constraint expression"); rc = -1; @@ -2961,10 +2955,35 @@ static int genfscon_to_cil(struct policydb *pdb) { struct genfs *genfs; struct ocontext *ocon; + uint32_t sclass; for (genfs = pdb->genfs; genfs != NULL; genfs = genfs->next) { for (ocon = genfs->head; ocon != NULL; ocon = ocon->next) { - cil_printf("(genfscon %s \"%s\" ", genfs->fstype, ocon->u.name); + sclass = ocon->v.sclass; + if (sclass) { + const char *file_type; + const char *class_name = pdb->p_class_val_to_name[sclass-1]; + if (strcmp(class_name, "file") == 0) { + file_type = "file"; + } else if (strcmp(class_name, "dir") == 0) { + file_type = "dir"; + } else if (strcmp(class_name, "chr_file") == 0) { + file_type = "char"; + } else if (strcmp(class_name, "blk_file") == 0) { + file_type = "block"; + } else if (strcmp(class_name, "sock_file") == 0) { + file_type = "socket"; + } else if (strcmp(class_name, "fifo_file") == 0) { + file_type = "pipe"; + } else if (strcmp(class_name, "lnk_file") == 0) { + file_type = "symlink"; + } else { + return -1; + } + cil_printf("(genfscon %s \"%s\" %s ", genfs->fstype, ocon->u.name, file_type); + } else { + cil_printf("(genfscon %s \"%s\" ", genfs->fstype, ocon->u.name); + } context_to_cil(pdb, &ocon->context[0]); cil_printf(")\n"); } @@ -4123,7 +4142,7 @@ exit: static int fp_to_buffer(FILE *fp, char **data, size_t *data_len) { int rc = -1; - char *d = NULL; + char *d = NULL, *d_tmp; size_t d_len = 0; size_t read_len = 0; size_t max_len = 1 << 17; // start at 128KB, this is enough to hold about half of all the existing pp files @@ -4139,12 +4158,13 @@ static int fp_to_buffer(FILE *fp, char **data, size_t *data_len) d_len += read_len; if (d_len == max_len) { max_len *= 2; - d = realloc(d, max_len); - if (d == NULL) { + d_tmp = realloc(d, max_len); + if (d_tmp == NULL) { log_err("Out of memory"); rc = -1; goto exit; } + d = d_tmp; } } diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c index 6826155c..93ff2116 100644 --- a/libsepol/src/optimize.c +++ b/libsepol/src/optimize.c @@ -31,6 +31,9 @@ #include <sepol/policydb/policydb.h> #include <sepol/policydb/conditional.h> +#include "debug.h" +#include "private.h" + #define TYPE_VEC_INIT_SIZE 16 struct type_vec { @@ -42,7 +45,7 @@ static int type_vec_init(struct type_vec *v) { v->capacity = TYPE_VEC_INIT_SIZE; v->count = 0; - v->types = malloc(v->capacity * sizeof(*v->types)); + v->types = mallocarray(v->capacity, sizeof(*v->types)); if (!v->types) return -1; return 0; @@ -57,8 +60,9 @@ static int type_vec_append(struct type_vec *v, uint32_t type) { if (v->capacity == v->count) { unsigned int new_capacity = v->capacity * 2; - uint32_t *new_types = realloc(v->types, - new_capacity * sizeof(*v->types)); + uint32_t *new_types = reallocarray(v->types, + new_capacity, + sizeof(*v->types)); if (!new_types) return -1; @@ -93,7 +97,7 @@ static struct type_vec *build_type_map(const policydb_t *p) { unsigned int i, k; ebitmap_node_t *n; - struct type_vec *map = malloc(p->p_types.nprim * sizeof(*map)); + struct type_vec *map = mallocarray(p->p_types.nprim, sizeof(*map)); if (!map) return NULL; @@ -101,6 +105,9 @@ static struct type_vec *build_type_map(const policydb_t *p) if (type_vec_init(&map[i])) goto err; + if (!p->type_val_to_struct[i]) + continue; + if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) { ebitmap_for_each_positive_bit(&p->type_attr_map[i], n, k) { @@ -111,11 +118,13 @@ static struct type_vec *build_type_map(const policydb_t *p) ebitmap_t *types_i = &p->attr_type_map[i]; for (k = 0; k < p->p_types.nprim; k++) { - ebitmap_t *types_k = &p->attr_type_map[k]; + const ebitmap_t *types_k; - if (p->type_val_to_struct[k]->flavor != TYPE_ATTRIB) + if (!p->type_val_to_struct[k] || p->type_val_to_struct[k]->flavor != TYPE_ATTRIB) continue; + types_k = &p->attr_type_map[k]; + if (ebitmap_contains(types_k, types_i)) { if (type_vec_append(&map[i], k)) goto err; @@ -435,6 +444,17 @@ int policydb_optimize(policydb_t *p) if (p->policy_type != POLICY_KERN) return -1; + if (p->policyvers >= POLICYDB_VERSION_AVTAB && p->policyvers <= POLICYDB_VERSION_PERMISSIVE) { + /* + * For policy versions between 20 and 23, attributes exist in the policy, + * but only in the type_attr_map. This means that there are gaps in both + * the type_val_to_struct and p_type_val_to_name arrays and policy rules + * can refer to those gaps. + */ + ERR(NULL, "Optimizing policy versions between 20 and 23 is not supported"); + return -1; + } + type_map = build_type_map(p); if (!type_map) return -1; diff --git a/libsepol/src/polcaps.c b/libsepol/src/polcaps.c index 6a74ec7d..687e971c 100644 --- a/libsepol/src/polcaps.c +++ b/libsepol/src/polcaps.c @@ -6,13 +6,14 @@ #include <sepol/policydb/polcaps.h> static const char * const polcap_names[] = { - "network_peer_controls", /* POLICYDB_CAPABILITY_NETPEER */ - "open_perms", /* POLICYDB_CAPABILITY_OPENPERM */ - "extended_socket_class", /* POLICYDB_CAPABILITY_EXTSOCKCLASS */ - "always_check_network", /* POLICYDB_CAPABILITY_ALWAYSNETWORK */ - "cgroup_seclabel", /* POLICYDB_CAPABILITY_SECLABEL */ - "nnp_nosuid_transition", /* POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION */ - "genfs_seclabel_symlinks", /* POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS */ + "network_peer_controls", /* POLICYDB_CAP_NETPEER */ + "open_perms", /* POLICYDB_CAP_OPENPERM */ + "extended_socket_class", /* POLICYDB_CAP_EXTSOCKCLASS */ + "always_check_network", /* POLICYDB_CAP_ALWAYSNETWORK */ + "cgroup_seclabel", /* POLICYDB_CAP_SECLABEL */ + "nnp_nosuid_transition", /* POLICYDB_CAP_NNP_NOSUID_TRANSITION */ + "genfs_seclabel_symlinks", /* POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS */ + "ioctl_skip_cloexec", /* POLICYDB_CAP_IOCTL_SKIP_CLOEXEC */ NULL }; @@ -20,7 +21,7 @@ int sepol_polcap_getnum(const char *name) { int capnum; - for (capnum = 0; capnum <= POLICYDB_CAPABILITY_MAX; capnum++) { + for (capnum = 0; capnum <= POLICYDB_CAP_MAX; capnum++) { if (polcap_names[capnum] == NULL) continue; if (strcasecmp(polcap_names[capnum], name) == 0) @@ -31,7 +32,7 @@ int sepol_polcap_getnum(const char *name) const char *sepol_polcap_getname(unsigned int capnum) { - if (capnum > POLICYDB_CAPABILITY_MAX) + if (capnum > POLICYDB_CAP_MAX) return NULL; return polcap_names[capnum]; diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c index 587ba64a..fc71463e 100644 --- a/libsepol/src/policydb.c +++ b/libsepol/src/policydb.c @@ -2103,6 +2103,8 @@ static int common_read(policydb_t * p, hashtab_t h, struct policy_file *fp) if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE)) goto bad; comdatum->permissions.nprim = le32_to_cpu(buf[2]); + if (comdatum->permissions.nprim > PERM_SYMTAB_SIZE) + goto bad; nel = le32_to_cpu(buf[3]); key = malloc(len + 1); @@ -2251,6 +2253,8 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp) if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE)) goto bad; cladatum->permissions.nprim = le32_to_cpu(buf[3]); + if (cladatum->permissions.nprim > PERM_SYMTAB_SIZE) + goto bad; nel = le32_to_cpu(buf[4]); ncons = le32_to_cpu(buf[5]); @@ -2679,7 +2683,10 @@ static int filename_trans_read_one_compat(policydb_t *p, struct policy_file *fp) if (rc < 0) goto err; - stype = le32_to_cpu(buf[0]); + stype = le32_to_cpu(buf[0]); + if (stype == 0) + goto err; + ttype = le32_to_cpu(buf[1]); tclass = le32_to_cpu(buf[2]); otype = le32_to_cpu(buf[3]); @@ -2773,6 +2780,7 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp) if (!datum) goto err; + datum->next = NULL; *dst = datum; /* ebitmap_read() will at least init the bitmap */ @@ -2790,7 +2798,6 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp) dst = &datum->next; } - *dst = NULL; if (ndatum > 1 && filename_trans_check_datum(first)) goto err; @@ -2879,6 +2886,8 @@ static int ocontext_read_xen(const struct policydb_compat_info *info, if (rc < 0) return -1; c->sid[0] = le32_to_cpu(buf[0]); + if (is_saturated(c->sid[0])) + return -1; if (context_read_and_validate (&c->context[0], p, fp)) return -1; @@ -2990,6 +2999,8 @@ static int ocontext_read_selinux(const struct policydb_compat_info *info, if (rc < 0) return -1; c->sid[0] = le32_to_cpu(buf[0]); + if (is_saturated(c->sid[0])) + return -1; if (context_read_and_validate (&c->context[0], p, fp)) return -1; @@ -3926,6 +3937,8 @@ static int scope_index_read(scope_index_t * scope_index, if (rc < 0) return -1; scope_index->class_perms_len = le32_to_cpu(buf[0]); + if (is_saturated(scope_index->class_perms_len)) + return -1; if (scope_index->class_perms_len == 0) { scope_index->class_perms_map = NULL; return 0; @@ -3980,6 +3993,8 @@ static int avrule_decl_read(policydb_t * p, avrule_decl_t * decl, if (rc < 0) return -1; nprim = le32_to_cpu(buf[0]); + if (is_saturated(nprim)) + return -1; nel = le32_to_cpu(buf[1]); for (j = 0; j < nel; j++) { if (read_f[i] (p, decl->symtab[i].table, fp)) { @@ -4106,12 +4121,12 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp) goto cleanup; scope->scope = le32_to_cpu(buf[0]); scope->decl_ids_len = le32_to_cpu(buf[1]); - if (scope->decl_ids_len == 0) { + if (zero_or_saturated(scope->decl_ids_len)) { ERR(fp->handle, "invalid scope with no declaration"); goto cleanup; } if ((scope->decl_ids = - malloc(scope->decl_ids_len * sizeof(uint32_t))) == NULL) { + mallocarray(scope->decl_ids_len, sizeof(uint32_t))) == NULL) { goto cleanup; } rc = next_entry(scope->decl_ids, fp, sizeof(uint32_t) * scope->decl_ids_len); @@ -4396,6 +4411,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose) if (rc < 0) goto bad; nprim = le32_to_cpu(buf[0]); + if (is_saturated(nprim)) + goto bad; nel = le32_to_cpu(buf[1]); if (nel && !nprim) { ERR(fp->handle, "unexpected items in symbol table with no symbol"); @@ -4500,8 +4517,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose) } if (policy_type == POLICY_KERN) { - p->type_attr_map = malloc(p->p_types.nprim * sizeof(ebitmap_t)); - p->attr_type_map = malloc(p->p_types.nprim * sizeof(ebitmap_t)); + p->type_attr_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t)); + p->attr_type_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t)); if (!p->type_attr_map || !p->attr_type_map) goto bad; for (i = 0; i < p->p_types.nprim; i++) { diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c index 5804d247..a2dcebe4 100644 --- a/libsepol/src/policydb_validate.c +++ b/libsepol/src/policydb_validate.c @@ -2,15 +2,24 @@ #include <sepol/policydb/conditional.h> #include <sepol/policydb/ebitmap.h> #include <sepol/policydb/policydb.h> +#include <sepol/policydb/services.h> #include "debug.h" #include "policydb_validate.h" +#define bool_xor(a, b) (!(a) != !(b)) +#define bool_xnor(a, b) !bool_xor(a, b) + typedef struct validate { uint32_t nprim; ebitmap_t gaps; } validate_t; +typedef struct map_arg { + validate_t *flavors; + sepol_handle_t *handle; + int mls; +} map_arg_t; static int create_gap_ebitmap(char **val_to_name, uint32_t nprim, ebitmap_t *gaps) { @@ -115,6 +124,30 @@ static int validate_type_set(type_set_t *type_set, validate_t *type) if (validate_ebitmap(&type_set->negset, type)) goto bad; + switch (type_set->flags) { + case 0: + case TYPE_STAR: + case TYPE_COMP: + break; + default: + goto bad; + } + + return 0; + +bad: + return -1; +} + +static int validate_empty_type_set(type_set_t *type_set) +{ + if (!ebitmap_is_empty(&type_set->types)) + goto bad; + if (!ebitmap_is_empty(&type_set->negset)) + goto bad; + if (type_set->flags != 0) + goto bad; + return 0; bad: @@ -124,9 +157,21 @@ bad: static int validate_role_set(role_set_t *role_set, validate_t *role) { if (validate_ebitmap(&role_set->roles, role)) - return -1; + goto bad; + + switch (role_set->flags) { + case 0: + case ROLE_STAR: + case ROLE_COMP: + break; + default: + goto bad; + } return 0; + +bad: + return -1; } static int validate_scope(__attribute__ ((unused)) hashtab_key_t k, hashtab_datum_t d, void *args) @@ -135,12 +180,23 @@ static int validate_scope(__attribute__ ((unused)) hashtab_key_t k, hashtab_datu uint32_t *nprim = (uint32_t *)args; unsigned int i; + switch (scope_datum->scope) { + case SCOPE_REQ: + case SCOPE_DECL: + break; + default: + goto bad; + } + for (i = 0; i < scope_datum->decl_ids_len; i++) { if (!value_isvalid(scope_datum->decl_ids[i], *nprim)) - return -1; + goto bad; } return 0; + +bad: + return -1; } static int validate_scopes(sepol_handle_t *handle, symtab_t scopes[], avrule_block_t *block) @@ -167,22 +223,110 @@ bad: return -1; } -static int validate_constraint_nodes(sepol_handle_t *handle, constraint_node_t *cons, validate_t flavors[]) +static int validate_constraint_nodes(sepol_handle_t *handle, unsigned int nperms, constraint_node_t *cons, validate_t flavors[]) { constraint_expr_t *cexp; for (; cons; cons = cons->next) { + if (nperms == 0 && cons->permissions != 0) + goto bad; + if (nperms > 0 && cons->permissions == 0) + goto bad; + if (nperms > 0 && nperms != PERM_SYMTAB_SIZE && cons->permissions >= (UINT32_C(1) << nperms)) + goto bad; + for (cexp = cons->expr; cexp; cexp = cexp->next) { - if (cexp->attr & CEXPR_USER) { - if (validate_ebitmap(&cexp->names, &flavors[SYM_USERS])) + if (cexp->expr_type == CEXPR_NAMES) { + if (cexp->attr & CEXPR_XTARGET && nperms != 0) goto bad; - } else if (cexp->attr & CEXPR_ROLE) { - if (validate_ebitmap(&cexp->names, &flavors[SYM_ROLES])) + if (!(cexp->attr & CEXPR_TYPE)) { + if (validate_empty_type_set(cexp->type_names)) + goto bad; + } + + switch (cexp->op) { + case CEXPR_EQ: + case CEXPR_NEQ: + break; + default: goto bad; - } else if (cexp->attr & CEXPR_TYPE) { - if (validate_ebitmap(&cexp->names, &flavors[SYM_TYPES])) + } + + switch (cexp->attr) { + case CEXPR_USER: + case CEXPR_USER | CEXPR_TARGET: + case CEXPR_USER | CEXPR_XTARGET: + if (validate_ebitmap(&cexp->names, &flavors[SYM_USERS])) + goto bad; + break; + case CEXPR_ROLE: + case CEXPR_ROLE | CEXPR_TARGET: + case CEXPR_ROLE | CEXPR_XTARGET: + if (validate_ebitmap(&cexp->names, &flavors[SYM_ROLES])) + goto bad; + break; + case CEXPR_TYPE: + case CEXPR_TYPE | CEXPR_TARGET: + case CEXPR_TYPE | CEXPR_XTARGET: + if (validate_ebitmap(&cexp->names, &flavors[SYM_TYPES])) + goto bad; + if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES])) + goto bad; + break; + default: + goto bad; + } + } else if (cexp->expr_type == CEXPR_ATTR) { + if (!ebitmap_is_empty(&cexp->names)) + goto bad; + if (validate_empty_type_set(cexp->type_names)) + goto bad; + + switch (cexp->op) { + case CEXPR_EQ: + case CEXPR_NEQ: + break; + case CEXPR_DOM: + case CEXPR_DOMBY: + case CEXPR_INCOMP: + if ((cexp->attr & CEXPR_USER) || (cexp->attr & CEXPR_TYPE)) + goto bad; + break; + default: goto bad; - if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES])) + } + + switch (cexp->attr) { + case CEXPR_USER: + case CEXPR_ROLE: + case CEXPR_TYPE: + case CEXPR_L1L2: + case CEXPR_L1H2: + case CEXPR_H1L2: + case CEXPR_H1H2: + case CEXPR_L1H1: + case CEXPR_L2H2: + break; + default: + goto bad; + } + } else { + switch (cexp->expr_type) { + case CEXPR_NOT: + case CEXPR_AND: + case CEXPR_OR: + break; + default: + goto bad; + } + + if (cexp->op != 0) + goto bad; + if (cexp->attr != 0) + goto bad; + if (!ebitmap_is_empty(&cexp->names)) + goto bad; + if (validate_empty_type_set(cexp->type_names)) goto bad; } } @@ -199,10 +343,53 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va { if (validate_value(class->s.value, &flavors[SYM_CLASSES])) goto bad; - if (validate_constraint_nodes(handle, class->constraints, flavors)) + if (class->permissions.nprim > PERM_SYMTAB_SIZE) + goto bad; + if (validate_constraint_nodes(handle, class->permissions.nprim, class->constraints, flavors)) goto bad; - if (validate_constraint_nodes(handle, class->validatetrans, flavors)) + if (validate_constraint_nodes(handle, 0, class->validatetrans, flavors)) + goto bad; + + switch (class->default_user) { + case 0: + case DEFAULT_SOURCE: + case DEFAULT_TARGET: + break; + default: + goto bad; + } + + switch (class->default_role) { + case 0: + case DEFAULT_SOURCE: + case DEFAULT_TARGET: + break; + default: + goto bad; + } + + switch (class->default_type) { + case 0: + case DEFAULT_SOURCE: + case DEFAULT_TARGET: + break; + default: + goto bad; + } + + switch (class->default_range) { + case 0: + case DEFAULT_SOURCE_LOW: + case DEFAULT_SOURCE_HIGH: + case DEFAULT_SOURCE_LOW_HIGH: + case DEFAULT_TARGET_LOW: + case DEFAULT_TARGET_HIGH: + case DEFAULT_TARGET_LOW_HIGH: + case DEFAULT_GLBLUB: + break; + default: goto bad; + } return 0; @@ -211,6 +398,32 @@ bad: return -1; } +static int validate_class_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args) +{ + map_arg_t *margs = args; + + return validate_class_datum(margs->handle, d, margs->flavors); +} + +static int validate_common_datum(sepol_handle_t *handle, common_datum_t *common) +{ + if (common->permissions.nprim > PERM_SYMTAB_SIZE) + goto bad; + + return 0; + +bad: + ERR(handle, "Invalid common class datum"); + return -1; +} + +static int validate_common_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args) +{ + map_arg_t *margs = args; + + return validate_common_datum(margs->handle, d); +} + static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, validate_t flavors[]) { if (validate_value(role->s.value, &flavors[SYM_ROLES])) @@ -227,10 +440,17 @@ static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, valid return 0; bad: - ERR(handle, "Invalid class datum"); + ERR(handle, "Invalid role datum"); return -1; } +static int validate_role_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args) +{ + map_arg_t *margs = args; + + return validate_role_datum(margs->handle, d, margs->flavors); +} + static int validate_type_datum(sepol_handle_t *handle, type_datum_t *type, validate_t flavors[]) { if (validate_value(type->s.value, &flavors[SYM_TYPES])) @@ -240,6 +460,26 @@ static int validate_type_datum(sepol_handle_t *handle, type_datum_t *type, valid if (type->bounds && validate_value(type->bounds, &flavors[SYM_TYPES])) goto bad; + switch (type->flavor) { + case TYPE_TYPE: + case TYPE_ATTRIB: + case TYPE_ALIAS: + break; + default: + goto bad; + } + + switch (type->flags) { + case 0: + case TYPE_FLAGS_PERMISSIVE: + case TYPE_FLAGS_EXPAND_ATTR_TRUE: + case TYPE_FLAGS_EXPAND_ATTR_FALSE: + case TYPE_FLAGS_EXPAND_ATTR: + break; + default: + goto bad; + } + return 0; bad: @@ -247,6 +487,13 @@ bad: return -1; } +static int validate_type_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args) +{ + map_arg_t *margs = args; + + return validate_type_datum(margs->handle, d, margs->flavors); +} + static int validate_mls_semantic_cat(mls_semantic_cat_t *cat, validate_t *cats) { for (; cat; cat = cat->next) { @@ -290,7 +537,41 @@ bad: return -1; } -static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[]) +static int validate_mls_level(mls_level_t *level, validate_t *sens, validate_t *cats) +{ + if (validate_value(level->sens, sens)) + goto bad; + if (validate_ebitmap(&level->cat, cats)) + goto bad; + + return 0; + + bad: + return -1; +} + +static int validate_level_datum(__attribute__ ((unused)) hashtab_key_t k, hashtab_datum_t d, void *args) +{ + level_datum_t *level = d; + validate_t *flavors = args; + + return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]); +} + +static int validate_mls_range(mls_range_t *range, validate_t *sens, validate_t *cats) +{ + if (validate_mls_level(&range->level[0], sens, cats)) + goto bad; + if (validate_mls_level(&range->level[1], sens, cats)) + goto bad; + + return 0; + + bad: + return -1; +} + +static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[], int mls) { if (validate_value(user->s.value, &flavors[SYM_USERS])) goto bad; @@ -300,6 +581,10 @@ static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, valid goto bad; if (validate_mls_semantic_level(&user->dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS])) goto bad; + if (mls && validate_mls_range(&user->exp_range, &flavors[SYM_LEVELS], &flavors[SYM_CATS])) + goto bad; + if (mls && validate_mls_level(&user->exp_dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS])) + goto bad; if (user->bounds && validate_value(user->bounds, &flavors[SYM_USERS])) goto bad; @@ -310,32 +595,60 @@ bad: return -1; } -static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate_t flavors[]) +static int validate_user_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args) +{ + map_arg_t *margs = args; + + return validate_user_datum(margs->handle, d, margs->flavors, margs->mls); +} + +static int validate_bool_datum(sepol_handle_t *handle, cond_bool_datum_t *boolean, validate_t flavors[]) +{ + if (validate_value(boolean->s.value, &flavors[SYM_BOOLS])) + goto bad; + + switch (boolean->state) { + case 0: + case 1: + break; + default: + goto bad; + } + + switch (boolean->flags) { + case 0: + case COND_BOOL_FLAGS_TUNABLE: + break; + default: + goto bad; + } + + return 0; + +bad: + ERR(handle, "Invalid bool datum"); + return -1; +} + +static int validate_bool_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args) +{ + map_arg_t *margs = args; + + return validate_bool_datum(margs->handle, d, margs->flavors); +} + +static int validate_datum_array_gaps(sepol_handle_t *handle, policydb_t *p, validate_t flavors[]) { unsigned int i; for (i = 0; i < p->p_classes.nprim; i++) { - if (p->class_val_to_struct[i]) { - if (ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i)) - goto bad; - if (validate_class_datum(handle, p->class_val_to_struct[i], flavors)) - goto bad; - } else { - if (!ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i)) - goto bad; - } + if (bool_xnor(p->class_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i))) + goto bad; } for (i = 0; i < p->p_roles.nprim; i++) { - if (p->role_val_to_struct[i]) { - if (ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i)) - goto bad; - if (validate_role_datum(handle, p->role_val_to_struct[i], flavors)) - goto bad; - } else { - if (!ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i)) - goto bad; - } + if (bool_xnor(p->role_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i))) + goto bad; } /* @@ -344,34 +657,68 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate */ if (p->policyvers < POLICYDB_VERSION_AVTAB || p->policyvers > POLICYDB_VERSION_PERMISSIVE) { for (i = 0; i < p->p_types.nprim; i++) { - if (p->type_val_to_struct[i]) { - if (ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i)) - goto bad; - if (validate_type_datum(handle, p->type_val_to_struct[i], flavors)) - goto bad; - } else { - if (!ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i)) - goto bad; - } + if (bool_xnor(p->type_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i))) + goto bad; } } for (i = 0; i < p->p_users.nprim; i++) { - if (p->user_val_to_struct[i]) { - if (ebitmap_get_bit(&flavors[SYM_USERS].gaps, i)) - goto bad; - if (validate_user_datum(handle, p->user_val_to_struct[i], flavors)) - goto bad; - } else { - if (!ebitmap_get_bit(&flavors[SYM_USERS].gaps, i)) - goto bad; - } + if (bool_xnor(p->user_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))) + goto bad; + } + + for (i = 0; i < p->p_bools.nprim; i++) { + if (bool_xnor(p->bool_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_BOOLS].gaps, i))) + goto bad; } return 0; bad: - ERR(handle, "Invalid datum arrays"); + ERR(handle, "Invalid datum array gaps"); + return -1; +} + +static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args) +{ + symtab_datum_t *s = d; + uint32_t *nprim = (uint32_t *)args; + + return !value_isvalid(s->value, *nprim); +} + +static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, validate_t flavors[]) +{ + map_arg_t margs = { flavors, handle, p->mls }; + + if (hashtab_map(p->p_commons.table, validate_common_datum_wrapper, &margs)) + goto bad; + + if (hashtab_map(p->p_classes.table, validate_class_datum_wrapper, &margs)) + goto bad; + + if (hashtab_map(p->p_roles.table, validate_role_datum_wrapper, &margs)) + goto bad; + + if (hashtab_map(p->p_types.table, validate_type_datum_wrapper, &margs)) + goto bad; + + if (hashtab_map(p->p_users.table, validate_user_datum_wrapper, &margs)) + goto bad; + + if (p->mls && hashtab_map(p->p_levels.table, validate_level_datum, flavors)) + goto bad; + + if (hashtab_map(p->p_cats.table, validate_datum, &flavors[SYM_CATS])) + goto bad; + + if (hashtab_map(p->p_bools.table, validate_bool_datum_wrapper, &margs)) + goto bad; + + return 0; + +bad: + ERR(handle, "Invalid datum array entries"); return -1; } @@ -379,7 +726,7 @@ bad: * Functions to validate a kernel policydb */ -static int validate_avtab_key(avtab_key_t *key, validate_t flavors[]) +static int validate_avtab_key(avtab_key_t *key, int conditional, validate_t flavors[]) { if (validate_value(key->source_type, &flavors[SYM_TYPES])) goto bad; @@ -387,6 +734,23 @@ static int validate_avtab_key(avtab_key_t *key, validate_t flavors[]) goto bad; if (validate_value(key->target_class, &flavors[SYM_CLASSES])) goto bad; + switch (0xFFF & key->specified) { + case AVTAB_ALLOWED: + case AVTAB_AUDITALLOW: + case AVTAB_AUDITDENY: + case AVTAB_TRANSITION: + case AVTAB_MEMBER: + case AVTAB_CHANGE: + break; + case AVTAB_XPERMS_ALLOWED: + case AVTAB_XPERMS_AUDITALLOW: + case AVTAB_XPERMS_DONTAUDIT: + if (conditional) + goto bad; + break; + default: + goto bad; + } return 0; @@ -394,15 +758,22 @@ bad: return -1; } -static int validate_avtab_key_wrapper(avtab_key_t *k, __attribute__ ((unused)) avtab_datum_t *d, void *args) +static int validate_avtab_key_and_datum(avtab_key_t *k, avtab_datum_t *d, void *args) { validate_t *flavors = (validate_t *)args; - return validate_avtab_key(k, flavors); + + if (validate_avtab_key(k, 0, flavors)) + return -1; + + if ((k->specified & AVTAB_TYPE) && validate_value(d->data, &flavors[SYM_TYPES])) + return -1; + + return 0; } static int validate_avtab(sepol_handle_t *handle, avtab_t *avtab, validate_t flavors[]) { - if (avtab_map(avtab, validate_avtab_key_wrapper, flavors)) { + if (avtab_map(avtab, validate_avtab_key_and_datum, flavors)) { ERR(handle, "Invalid avtab"); return -1; } @@ -416,7 +787,7 @@ static int validate_cond_av_list(sepol_handle_t *handle, cond_av_list_t *cond_av for (; cond_av; cond_av = cond_av->next) { for (avtab_ptr = cond_av->node; avtab_ptr; avtab_ptr = avtab_ptr->next) { - if (validate_avtab_key(&avtab_ptr->key, flavors)) { + if (validate_avtab_key(&avtab_ptr->key, 1, flavors)) { ERR(handle, "Invalid cond av list"); return -1; } @@ -426,7 +797,7 @@ static int validate_cond_av_list(sepol_handle_t *handle, cond_av_list_t *cond_av return 0; } -static int validate_avrules(sepol_handle_t *handle, avrule_t *avrule, validate_t flavors[]) +static int validate_avrules(sepol_handle_t *handle, avrule_t *avrule, int conditional, validate_t flavors[]) { class_perm_node_t *class; @@ -440,6 +811,48 @@ static int validate_avrules(sepol_handle_t *handle, avrule_t *avrule, validate_t if (validate_value(class->tclass, &flavors[SYM_CLASSES])) goto bad; } + + switch(avrule->specified) { + case AVRULE_ALLOWED: + case AVRULE_AUDITALLOW: + case AVRULE_AUDITDENY: + case AVRULE_DONTAUDIT: + case AVRULE_TRANSITION: + case AVRULE_MEMBER: + case AVRULE_CHANGE: + break; + case AVRULE_NEVERALLOW: + case AVRULE_XPERMS_ALLOWED: + case AVRULE_XPERMS_AUDITALLOW: + case AVRULE_XPERMS_DONTAUDIT: + case AVRULE_XPERMS_NEVERALLOW: + if (conditional) + goto bad; + break; + default: + goto bad; + } + + if (avrule->specified & AVRULE_XPERMS) { + if (!avrule->xperms) + goto bad; + switch (avrule->xperms->specified) { + case AVRULE_XPERMS_IOCTLFUNCTION: + case AVRULE_XPERMS_IOCTLDRIVER: + break; + default: + goto bad; + } + } else if (avrule->xperms) + goto bad; + + switch(avrule->flags) { + case 0: + case RULE_SELF: + break; + default: + goto bad; + } } return 0; @@ -475,9 +888,9 @@ static int validate_cond_list(sepol_handle_t *handle, cond_list_t *cond, validat goto bad; if (validate_cond_av_list(handle, cond->false_list, flavors)) goto bad; - if (validate_avrules(handle, cond->avtrue_list, flavors)) + if (validate_avrules(handle, cond->avtrue_list, 1, flavors)) goto bad; - if (validate_avrules(handle, cond->avfalse_list, flavors)) + if (validate_avrules(handle, cond->avfalse_list, 1, flavors)) goto bad; if (validate_bool_id_array(handle, cond->bool_ids, cond->nbools, &flavors[SYM_BOOLS])) goto bad; @@ -559,6 +972,77 @@ static int validate_filename_trans_hashtab(sepol_handle_t *handle, hashtab_t fil return 0; } +static int validate_context(context_struct_t *con, validate_t flavors[], int mls) +{ + if (validate_value(con->user, &flavors[SYM_USERS])) + return -1; + if (validate_value(con->role, &flavors[SYM_ROLES])) + return -1; + if (validate_value(con->type, &flavors[SYM_TYPES])) + return -1; + if (mls && validate_mls_range(&con->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS])) + return -1; + + return 0; +} + +static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t flavors[]) +{ + ocontext_t *octx; + unsigned int i; + + for (i = 0; i < OCON_NUM; i++) { + for (octx = p->ocontexts[i]; octx; octx = octx->next) { + if (validate_context(&octx->context[0], flavors, p->mls)) + goto bad; + + if (p->target_platform == SEPOL_TARGET_SELINUX) { + switch (i) { + case OCON_FS: + case OCON_NETIF: + if (validate_context(&octx->context[1], flavors, p->mls)) + goto bad; + break; + case OCON_FSUSE: + switch (octx->v.behavior) { + case SECURITY_FS_USE_XATTR: + case SECURITY_FS_USE_TRANS: + case SECURITY_FS_USE_TASK: + break; + default: + goto bad; + } + } + } + } + } + + return 0; + +bad: + ERR(handle, "Invalid ocontext"); + return -1; +} + +static int validate_genfs(sepol_handle_t *handle, policydb_t *p, validate_t flavors[]) +{ + genfs_t *genfs; + ocontext_t *octx; + + for (genfs = p->genfs; genfs; genfs = genfs->next) { + for (octx = genfs->head; octx; octx = octx->next) { + if (validate_context(&octx->context[0], flavors, p->mls)) + goto bad; + } + } + + return 0; + +bad: + ERR(handle, "Invalid genfs"); + return -1; +} + /* * Functions to validate a module policydb */ @@ -666,14 +1150,6 @@ bad: return -1; } -static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args) -{ - symtab_datum_t *s = d; - uint32_t *nprim = (uint32_t *)args; - - return !value_isvalid(s->value, *nprim); -} - static int validate_symtabs(sepol_handle_t *handle, symtab_t symtabs[], validate_t flavors[]) { unsigned int i; @@ -696,7 +1172,7 @@ static int validate_avrule_blocks(sepol_handle_t *handle, avrule_block_t *avrule for (decl = avrule_block->branch_list; decl != NULL; decl = decl->next) { if (validate_cond_list(handle, decl->cond_list, flavors)) goto bad; - if (validate_avrules(handle, decl->avrules, flavors)) + if (validate_avrules(handle, decl->avrules, 0, flavors)) goto bad; if (validate_role_trans_rules(handle, decl->role_tr_rules, flavors)) goto bad; @@ -713,6 +1189,14 @@ static int validate_avrule_blocks(sepol_handle_t *handle, avrule_block_t *avrule if (validate_symtabs(handle, decl->symtab, flavors)) goto bad; } + + switch (avrule_block->flags) { + case 0: + case AVRULE_OPTIONAL: + break; + default: + goto bad; + } } return 0; @@ -722,6 +1206,71 @@ bad: return -1; } +static int validate_permissives(sepol_handle_t *handle, policydb_t *p, validate_t flavors[]) +{ + ebitmap_node_t *node; + unsigned i; + + ebitmap_for_each_positive_bit(&p->permissive_map, node, i) { + if (validate_value(i, &flavors[SYM_TYPES])) + goto bad; + } + + return 0; + +bad: + ERR(handle, "Invalid permissive type"); + return -1; +} + +static int validate_properties(sepol_handle_t *handle, policydb_t *p) +{ + switch (p->policy_type) { + case POLICY_KERN: + if (p->policyvers < POLICYDB_VERSION_MIN || p->policyvers > POLICYDB_VERSION_MAX) + goto bad; + break; + case POLICY_BASE: + case POLICY_MOD: + if (p->policyvers < MOD_POLICYDB_VERSION_MIN || p->policyvers > MOD_POLICYDB_VERSION_MAX) + goto bad; + break; + default: + goto bad; + } + + switch (p->target_platform) { + case SEPOL_TARGET_SELINUX: + case SEPOL_TARGET_XEN: + break; + default: + goto bad; + } + + switch (p->mls) { + case 0: + case 1: + break; + default: + goto bad; + } + + switch (p->handle_unknown) { + case SEPOL_DENY_UNKNOWN: + case SEPOL_REJECT_UNKNOWN: + case SEPOL_ALLOW_UNKNOWN: + break; + default: + goto bad; + } + + return 0; + +bad: + ERR(handle, "Invalid policy property"); + return -1; +} + static void validate_array_destroy(validate_t flavors[]) { unsigned int i; @@ -741,6 +1290,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p) if (validate_array_init(p, flavors)) goto bad; + if (validate_properties(handle, p)) + goto bad; + if (p->policy_type == POLICY_KERN) { if (validate_avtab(handle, &p->te_avtab, flavors)) goto bad; @@ -759,10 +1311,22 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p) goto bad; } + if (validate_ocontexts(handle, p, flavors)) + goto bad; + + if (validate_genfs(handle, p, flavors)) + goto bad; + if (validate_scopes(handle, p->scope, p->global)) goto bad; - if (validate_datum_arrays(handle, p, flavors)) + if (validate_datum_array_gaps(handle, p, flavors)) + goto bad; + + if (validate_datum_array_entries(handle, p, flavors)) + goto bad; + + if (validate_permissives(handle, p, flavors)) goto bad; validate_array_destroy(flavors); diff --git a/libsepol/src/private.h b/libsepol/src/private.h index 71287282..a8cc1472 100644 --- a/libsepol/src/private.h +++ b/libsepol/src/private.h @@ -44,7 +44,12 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) -#define is_saturated(x) (x == (typeof(x))-1) +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +# define is_saturated(x) (x == (typeof(x))-1 || (x) > (1U << 16)) +#else +# define is_saturated(x) (x == (typeof(x))-1) +#endif + #define zero_or_saturated(x) ((x == 0) || is_saturated(x)) #define spaceship_cmp(a, b) (((a) > (b)) - ((a) < (b))) @@ -78,3 +83,23 @@ extern int next_entry(void *buf, struct policy_file *fp, size_t bytes); extern size_t put_entry(const void *ptr, size_t size, size_t n, struct policy_file *fp); extern int str_read(char **strp, struct policy_file *fp, size_t len); + +static inline void* mallocarray(size_t nmemb, size_t size) { + if (size && nmemb > (size_t)-1 / size) { + errno = ENOMEM; + return NULL; + } + + return malloc(nmemb * size); +} + +#ifndef HAVE_REALLOCARRAY +static inline void* reallocarray(void *ptr, size_t nmemb, size_t size) { + if (size && nmemb > (size_t)-1 / size) { + errno = ENOMEM; + return NULL; + } + + return realloc(ptr, nmemb * size); +} +#endif diff --git a/libsepol/src/services.c b/libsepol/src/services.c index 3407058f..29723729 100644 --- a/libsepol/src/services.c +++ b/libsepol/src/services.c @@ -94,7 +94,7 @@ static void push(char *expr_ptr) else new_stack_len = stack_len * 2; - new_stack = realloc(stack, new_stack_len * sizeof(*stack)); + new_stack = reallocarray(stack, new_stack_len, sizeof(*stack)); if (!new_stack) { ERR(NULL, "unable to allocate stack space"); return; @@ -449,8 +449,8 @@ static int constraint_expr_eval_reason(context_struct_t *scontext, else new_expr_list_len = expr_list_len * 2; - new_expr_list = realloc(expr_list, - new_expr_list_len * sizeof(*expr_list)); + new_expr_list = reallocarray(expr_list, + new_expr_list_len, sizeof(*expr_list)); if (!new_expr_list) { ERR(NULL, "failed to allocate expr buffer stack"); rc = -ENOMEM; @@ -712,7 +712,7 @@ mls_ops: * Generate the same number of answer buffer entries as expression * buffers (as there will never be more). */ - answer_list = malloc(expr_count * sizeof(*answer_list)); + answer_list = mallocarray(expr_count, sizeof(*answer_list)); if (!answer_list) { ERR(NULL, "failed to allocate answer stack"); rc = -ENOMEM; @@ -797,7 +797,7 @@ mls_ops: for (x = 0; buffers[x] != NULL; x++) { while (1) { - p = *r_buf + reason_buf_used; + p = *r_buf ? (*r_buf + reason_buf_used) : NULL; len = snprintf(p, reason_buf_len - reason_buf_used, "%s", buffers[x]); if (len < 0 || len >= reason_buf_len - reason_buf_used) { @@ -1553,7 +1553,7 @@ static int validate_class(hashtab_key_t key, hashtab_datum_t datum, void *p) cladatum2->comdatum->permissions.table)) { ERR(NULL, " in the access vector definition " - "for class %s\n", key); + "for class %s", key); return -1; } } @@ -2163,7 +2163,7 @@ int sepol_get_user_sids(sepol_security_id_t fromsid, } usercon.user = user->s.value; - mysids = malloc(maxnel * sizeof(sepol_security_id_t)); + mysids = mallocarray(maxnel, sizeof(sepol_security_id_t)); if (!mysids) { rc = -ENOMEM; goto out; @@ -2199,7 +2199,7 @@ int sepol_get_user_sids(sepol_security_id_t fromsid, } else { maxnel += SIDS_NEL; mysids2 = - malloc(maxnel * + mallocarray(maxnel, sizeof(sepol_security_id_t)); if (!mysids2) { diff --git a/libsepol/src/sidtab.c b/libsepol/src/sidtab.c index 255e0725..adeae6eb 100644 --- a/libsepol/src/sidtab.c +++ b/libsepol/src/sidtab.c @@ -15,6 +15,7 @@ #include <sepol/policydb/sidtab.h> #include "flask.h" +#include "private.h" #define SIDTAB_HASH(sid) \ (sid & SIDTAB_HASH_MASK) @@ -27,7 +28,7 @@ int sepol_sidtab_init(sidtab_t * s) { int i; - s->htable = malloc(sizeof(sidtab_ptr_t) * SIDTAB_SIZE); + s->htable = mallocarray(SIDTAB_SIZE, sizeof(sidtab_ptr_t)); if (!s->htable) return -ENOMEM; for (i = 0; i < SIDTAB_SIZE; i++) diff --git a/libsepol/src/user_record.c b/libsepol/src/user_record.c index ac520060..404fa3a8 100644 --- a/libsepol/src/user_record.c +++ b/libsepol/src/user_record.c @@ -4,6 +4,7 @@ #include "user_internal.h" #include "debug.h" +#include "private.h" struct sepol_user { /* This user's name */ @@ -182,8 +183,9 @@ int sepol_user_add_role(sepol_handle_t * handle, if (!role_cp) goto omem; - roles_realloc = realloc(user->roles, - sizeof(char *) * (user->num_roles + 1)); + roles_realloc = reallocarray(user->roles, + user->num_roles + 1, + sizeof(char *)); if (!roles_realloc) goto omem; @@ -265,7 +267,7 @@ int sepol_user_get_roles(sepol_handle_t * handle, unsigned int i; const char **tmp_roles = - (const char **)malloc(sizeof(char *) * user->num_roles); + (const char **)mallocarray(user->num_roles, sizeof(char *)); if (!tmp_roles) goto omem; diff --git a/libsepol/src/users.c b/libsepol/src/users.c index b895b7f5..a7406214 100644 --- a/libsepol/src/users.c +++ b/libsepol/src/users.c @@ -226,17 +226,17 @@ int sepol_user_modify(sepol_handle_t * handle, void *tmp_ptr; /* Ensure reverse lookup array has enough space */ - tmp_ptr = realloc(policydb->user_val_to_struct, - (policydb->p_users.nprim + - 1) * sizeof(user_datum_t *)); + tmp_ptr = reallocarray(policydb->user_val_to_struct, + policydb->p_users.nprim + 1, + sizeof(user_datum_t *)); if (!tmp_ptr) goto omem; policydb->user_val_to_struct = tmp_ptr; policydb->user_val_to_struct[policydb->p_users.nprim] = NULL; - tmp_ptr = realloc(policydb->sym_val_to_name[SYM_USERS], - (policydb->p_users.nprim + - 1) * sizeof(char *)); + tmp_ptr = reallocarray(policydb->sym_val_to_name[SYM_USERS], + policydb->p_users.nprim + 1, + sizeof(char *)); if (!tmp_ptr) goto omem; policydb->sym_val_to_name[SYM_USERS] = tmp_ptr; diff --git a/libsepol/src/util.c b/libsepol/src/util.c index 902c63c5..1cd1308d 100644 --- a/libsepol/src/util.c +++ b/libsepol/src/util.c @@ -28,6 +28,8 @@ #include <sepol/policydb/policydb.h> #include <sepol/policydb/util.h> +#include "private.h" + struct val_to_name { unsigned int val; char *name; @@ -40,6 +42,8 @@ struct val_to_name { * 0). Return 0 on success, -1 on out of memory. */ int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a) { + uint32_t *new; + if (cnt == NULL || a == NULL) return -1; @@ -48,17 +52,18 @@ int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a) * than be smart about it, for now we realloc() the array each * time a new uint32_t is added! */ if (*a != NULL) - *a = (uint32_t *) realloc(*a, (*cnt + 1) * sizeof(uint32_t)); + new = (uint32_t *) reallocarray(*a, *cnt + 1, sizeof(uint32_t)); else { /* empty list */ *cnt = 0; - *a = (uint32_t *) malloc(sizeof(uint32_t)); + new = (uint32_t *) malloc(sizeof(uint32_t)); } - if (*a == NULL) { + if (new == NULL) { return -1; } - (*a)[*cnt] = i; + new[*cnt] = i; (*cnt)++; + *a = new; return 0; } diff --git a/libsepol/src/write.c b/libsepol/src/write.c index b09551f7..db244c08 100644 --- a/libsepol/src/write.c +++ b/libsepol/src/write.c @@ -2117,7 +2117,7 @@ static int scope_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) * buffer. this would have been easier with C99's * dynamic arrays... */ rc = POLICYDB_ERROR; - dyn_buf = malloc(items * sizeof(*dyn_buf)); + dyn_buf = mallocarray(items, sizeof(*dyn_buf)); if (!dyn_buf) goto err; buf = dyn_buf; diff --git a/libsepol/tests/Makefile b/libsepol/tests/Makefile index fc9bd1a3..a72c327d 100644 --- a/libsepol/tests/Makefile +++ b/libsepol/tests/Makefile @@ -1,3 +1,4 @@ +ENV ?= env M4 ?= m4 MKDIR ?= mkdir EXE ?= libsepol-tests @@ -44,10 +45,15 @@ clean: rm -f $(objs) $(EXE) rm -f $(policies) rm -f policies/test-downgrade/policy.hi policies/test-downgrade/policy.lo - +# mkdir is run in a clean environment created by env -i to avoid failing under ASan with: +# +# ASan runtime does not come first in initial library list; +# you should either link runtime to your application or manually preload it with LD_PRELOAD +# +# when the source code is built with ASan test: $(EXE) $(policies) - $(MKDIR) -p policies/test-downgrade + $(ENV) -i $(MKDIR) -p policies/test-downgrade ../../checkpolicy/checkpolicy -M policies/test-cond/refpolicy-base.conf -o policies/test-downgrade/policy.hi ./$(EXE) diff --git a/mcstrans/Makefile b/mcstrans/Makefile index c993a9f5..b20279ab 100644 --- a/mcstrans/Makefile +++ b/mcstrans/Makefile @@ -1,3 +1,9 @@ +PKG_CONFIG ?= pkg-config +PCRE_MODULE := libpcre2-8 +PCRE_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PCRE_MODULE)) -DPCRE2_CODE_UNIT_WIDTH=8 +PCRE_LDLIBS := $(shell $(PKG_CONFIG) --libs $(PCRE_MODULE)) +export PCRE_MODULE PCRE_CFLAGS PCRE_LDLIBS + all: $(MAKE) -C src $(MAKE) -C utils diff --git a/mcstrans/src/Makefile b/mcstrans/src/Makefile index 76ef0557..ef518625 100644 --- a/mcstrans/src/Makefile +++ b/mcstrans/src/Makefile @@ -20,10 +20,10 @@ CFLAGS ?= -Wall -W -Wundef -Wmissing-noreturn -Wmissing-format-attribute all: $(PROG) $(PROG): $(PROG_OBJS) $(LIBSEPOLA) - $(CC) $(LDFLAGS) -pie -o $@ $^ -lselinux -lcap -lpcre $(LDLIBS_LIBSEPOLA) + $(CC) $(LDFLAGS) -pie -o $@ $^ -lselinux -lcap $(PCRE_LDLIBS) $(LDLIBS_LIBSEPOLA) %.o: %.c - $(CC) $(CFLAGS) -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -fPIE -c -o $@ $< + $(CC) $(CFLAGS) $(PCRE_CFLAGS) -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -fPIE -c -o $@ $< install: all test -d $(DESTDIR)$(SBINDIR) || install -m 755 -d $(DESTDIR)$(SBINDIR) diff --git a/mcstrans/src/mcscolor.c b/mcstrans/src/mcscolor.c index a3838850..9ff0ce2f 100644 --- a/mcstrans/src/mcscolor.c +++ b/mcstrans/src/mcscolor.c @@ -11,6 +11,8 @@ #include <syslog.h> #include <selinux/selinux.h> #include <selinux/context.h> + +#include "mcscolor.h" #include "mcstrans.h" /* Define data structures */ diff --git a/mcstrans/src/mcscolor.h b/mcstrans/src/mcscolor.h new file mode 100644 index 00000000..c37fe6ed --- /dev/null +++ b/mcstrans/src/mcscolor.h @@ -0,0 +1,8 @@ +#ifndef __mcscolor_h__ +#define __mcscolor_h__ + +extern void finish_context_colors(void); +extern int init_colors(void); +extern int raw_color(const char *raw, char **color_str); + +#endif diff --git a/mcstrans/src/mcstrans.c b/mcstrans/src/mcstrans.c index e92dfddb..d42760fd 100644 --- a/mcstrans/src/mcstrans.c +++ b/mcstrans/src/mcstrans.c @@ -26,7 +26,7 @@ #include <selinux/context.h> #include <syslog.h> #include <errno.h> -#include <pcre.h> +#include <pcre2.h> #include <ctype.h> #include <time.h> #include <sys/time.h> @@ -36,7 +36,6 @@ #include "mcstrans.h" #define N_BUCKETS 1453 -#define OVECCOUNT (512*3) #define log_error(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) @@ -82,9 +81,9 @@ typedef struct word_group { affix_t *suffixes; word_t *words; - pcre *prefix_regexp; - pcre *word_regexp; - pcre *suffix_regexp; + pcre2_code *prefix_regexp; + pcre2_code *word_regexp; + pcre2_code *suffix_regexp; ebitmap_t def; @@ -109,7 +108,7 @@ typedef struct domain { base_classification_t *base_classifications; word_group_t *groups; - pcre *base_classification_regexp; + pcre2_code *base_classification_regexp; struct domain *next; } domain_t; @@ -136,7 +135,7 @@ typedef struct cat_constraint { static cat_constraint_t *cat_constraints; -unsigned int +static unsigned int hash(const char *str) { unsigned int hash = 5381; int c; @@ -213,7 +212,7 @@ parse_category(ebitmap_t *e, const char *raw, int allowinverse) return 0; } -int +static int parse_ebitmap(ebitmap_t *e, ebitmap_t *def, const char *raw) { int rc = ebitmap_cpy(e, def); if (rc < 0) @@ -224,7 +223,7 @@ parse_ebitmap(ebitmap_t *e, ebitmap_t *def, const char *raw) { return 0; } -mls_level_t * +static mls_level_t * parse_raw(const char *raw) { mls_level_t *mls = calloc(1, sizeof(mls_level_t)); if (!mls) @@ -248,7 +247,7 @@ err: return NULL; } -void +static void destroy_word(word_t **list, word_t *word) { if (!word) { return; @@ -267,7 +266,7 @@ destroy_word(word_t **list, word_t *word) { free(word); } -word_t * +static word_t * create_word(word_t **list, const char *text) { word_t *w = calloc(1, sizeof(word_t)); if (!w) { @@ -291,7 +290,7 @@ err: return NULL; } -void +static void destroy_group(word_group_t **list, word_group_t *group) { for (; list && *list; list = &(*list)->next) { if (*list == group) { @@ -317,14 +316,14 @@ destroy_group(word_group_t **list, word_group_t *group) { free(group->name); free(group->sword); free(group->join); - pcre_free(group->prefix_regexp); - pcre_free(group->word_regexp); - pcre_free(group->suffix_regexp); + pcre2_code_free(group->prefix_regexp); + pcre2_code_free(group->word_regexp); + pcre2_code_free(group->suffix_regexp); ebitmap_destroy(&group->def); free(group); } -word_group_t * +static word_group_t * create_group(word_group_t **list, const char *name) { word_group_t *group = calloc(1, sizeof(word_group_t)); if (!group) @@ -357,7 +356,7 @@ err: return NULL; } -void +static void destroy_domain(domain_t *domain) { int i; unsigned int rt = 0, tr = 0; @@ -392,7 +391,7 @@ destroy_domain(domain_t *domain) { free(domain->base_classifications); domain->base_classifications = next; } - pcre_free(domain->base_classification_regexp); + pcre2_code_free(domain->base_classification_regexp); while (domain->groups) destroy_group(&domain->groups, domain->groups); free(domain->name); @@ -401,7 +400,7 @@ destroy_domain(domain_t *domain) { syslog(LOG_INFO, "cache sizes: tr = %u, rt = %u", tr, rt); } -domain_t * +static domain_t * create_domain(const char *name) { domain_t *domain = calloc(1, sizeof(domain_t)); if (!domain) { @@ -425,7 +424,7 @@ err: return NULL; } -int +static int add_word(word_group_t *group, char *raw, char *trans) { if (strchr(trans,'-')) { log_error("'%s'is invalid because '-' is illegal in modifiers.\n", trans); @@ -451,7 +450,7 @@ add_word(word_group_t *group, char *raw, char *trans) { return 0; } -int +static int add_constraint(char op, char *raw, char *tok) { log_debug("%s\n", "add_constraint"); ebitmap_t empty; @@ -521,7 +520,7 @@ add_constraint(char op, char *raw, char *tok) { return 0; } -int +static int violates_constraints(mls_level_t *l) { int nbits; sens_constraint_t *s; @@ -563,7 +562,7 @@ violates_constraints(mls_level_t *l) { return 0; } -void +static void destroy_sens_constraint(sens_constraint_t **list, sens_constraint_t *constraint) { if (!constraint) { return; @@ -580,7 +579,7 @@ destroy_sens_constraint(sens_constraint_t **list, sens_constraint_t *constraint) free(constraint); } -void +static void destroy_cat_constraint(cat_constraint_t **list, cat_constraint_t *constraint) { if (!constraint) { return; @@ -663,7 +662,7 @@ find_in_table(context_map_node_t **table, const char *key) { return NULL; } -char * +static char * trim(char *str, const char *whitespace) { char *p = str + strlen(str); @@ -672,7 +671,7 @@ trim(char *str, const char *whitespace) { return str; } -char * +static char * triml(char *str, const char *whitespace) { char *p = str; @@ -681,7 +680,7 @@ triml(char *str, const char *whitespace) { return p; } -int +static int update(char **p, char *const val) { free (*p); *p = strdup(val); @@ -692,7 +691,7 @@ update(char **p, char *const val) { return 0; } -int +static int append(affix_t **affixes, const char *val) { affix_t *affix = calloc(1, sizeof(affix_t)); if (!affix) { @@ -887,7 +886,7 @@ init_translations(void) { return(read_translations(selinux_translations_path())); } -char * +static char * extract_range(const char *incon) { context_t con = context_new(incon); if (!con) { @@ -910,7 +909,7 @@ extract_range(const char *incon) { return r; } -char * +static char * new_context_str(const char *incon, const char *range) { char *rcon = NULL; context_t con = context_new(incon); @@ -931,7 +930,7 @@ exit: return NULL; } -char * +static char * find_in_hashtable(const char *range, domain_t *domain, context_map_node_t **table) { char *trans = NULL; context_map_t *map = find_in_table(table, range); @@ -946,13 +945,6 @@ find_in_hashtable(const char *range, domain_t *domain, context_map_node_t **tabl return trans; } -void -emit_whitespace(char*buffer, char *whitespace) { - strcat(buffer, "["); - strcat(buffer, whitespace); - strcat(buffer, "]"); -} - static int string_size(const void *p1, const void *p2) { return strlen(*(char **)p2) - strlen(*(char **)p1); @@ -969,20 +961,22 @@ word_size(const void *p1, const void *p2) { return (w2_len - w1_len); } -void -build_regexp(pcre **r, char *buffer) { - const char *error; - int error_offset; +static void +build_regexp(pcre2_code **r, char *buffer) { + int error; + PCRE2_SIZE error_offset; if (*r) - pcre_free(*r); - *r = pcre_compile(buffer, PCRE_CASELESS, &error, &error_offset, NULL); - if (error) { - log_error("pcre=%s, error=%s\n", buffer, error ? error: "none"); + pcre2_code_free(*r); + *r = pcre2_compile((PCRE2_SPTR8) buffer, PCRE2_ZERO_TERMINATED, PCRE2_CASELESS, &error, &error_offset, NULL); + if (!*r) { + PCRE2_UCHAR errbuf[256]; + pcre2_get_error_message(error, errbuf, sizeof(errbuf)); + log_error("pcre compilation of '%s' failed at offset %zu: %s\n", buffer, error_offset, errbuf); } buffer[0] = '\0'; } -int +static int build_regexps(domain_t *domain) { char buffer[1024 * 128]; buffer[0] = '\0'; @@ -1086,7 +1080,7 @@ build_regexps(domain_t *domain) { return 0; } -char * +static char * compute_raw_from_trans(const char *level, domain_t *domain) { #ifdef DEBUG @@ -1095,12 +1089,12 @@ compute_raw_from_trans(const char *level, domain_t *domain) { #endif int rc = 0; - int ovector[OVECCOUNT]; + pcre2_match_data *match_data = NULL; word_group_t *g = NULL; char *work = NULL; char *r = NULL; - const char * match = NULL; - int work_len; + char *match = NULL; + size_t work_len; mls_level_t *mraw = NULL; ebitmap_t set, clear, tmp; @@ -1121,11 +1115,20 @@ compute_raw_from_trans(const char *level, domain_t *domain) { if (!domain->base_classification_regexp) goto err; log_debug(" compute_raw_from_trans work = %s\n", work); - rc = pcre_exec(domain->base_classification_regexp, 0, work, work_len, 0, PCRE_ANCHORED, ovector, OVECCOUNT); + match_data = pcre2_match_data_create_from_pattern(domain->base_classification_regexp, NULL); + if (!match_data) { + log_error("allocation error %s", strerror(errno)); + goto err; + } + rc = pcre2_match(domain->base_classification_regexp, (PCRE2_SPTR8)work, work_len, 0, PCRE2_ANCHORED, match_data, NULL); if (rc > 0) { - match = NULL; - pcre_get_substring(work, ovector, rc, 0, &match); - log_debug(" compute_raw_from_trans match = %s len = %u\n", match, strlen(match)); + const PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); + match = strndup(work + ovector[0], ovector[1] - ovector[0]); + if (!match) { + log_error("allocation error %s", strerror(errno)); + goto err; + } + log_debug(" compute_raw_from_trans match = %s len = %zu\n", match, strlen(match)); base_classification_t *bc; for (bc = domain->base_classifications; bc; bc = bc->next) { if (!strcmp(bc->trans, match)) { @@ -1145,12 +1148,23 @@ compute_raw_from_trans(const char *level, domain_t *domain) { char *p=work + ovector[0] + ovector[1]; while (*p && (strchr(" ", *p) != NULL)) *p++ = '#'; - pcre_free((char *)match); + + free(match); match = NULL; } else { - log_debug(" compute_raw_from_trans no base classification matched %s\n", level); + switch (rc) { + case PCRE2_ERROR_NOMATCH: + log_debug(" compute_raw_from_trans no base classification matched %s\n", level); + break; + default: + log_error("compute_raw_from_trans: base matching error for input '%s': %d\n", level, rc); + break; + } } + pcre2_match_data_free(match_data); + match_data = NULL; + if (mraw == NULL) { goto err; } @@ -1161,23 +1175,43 @@ compute_raw_from_trans(const char *level, domain_t *domain) { change = 0; for (g = domain->groups; g && !change && !complete; g = g->next) { int prefix = 0, suffix = 0; - int prefix_offset = 0, prefix_len = 0; - int suffix_offset = 0, suffix_len = 0; + PCRE2_SIZE prefix_offset = 0, prefix_len = 0; + PCRE2_SIZE suffix_offset = 0, suffix_len = 0; if (g->prefix_regexp) { - rc = pcre_exec(g->prefix_regexp, 0, work, work_len, 0, 0, ovector, OVECCOUNT); + match_data = pcre2_match_data_create_from_pattern(g->prefix_regexp, NULL); + if (!match_data) { + log_error("allocation error %s", strerror(errno)); + goto err; + } + rc = pcre2_match(g->prefix_regexp, (PCRE2_SPTR8)work, work_len, 0, 0, match_data, NULL); if (rc > 0) { + const PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); prefix = 1; prefix_offset = ovector[0]; prefix_len = ovector[1] - ovector[0]; + } else if (rc != PCRE2_ERROR_NOMATCH) { + log_error("compute_raw_from_trans: prefix matching error for input '%s': %d\n", level, rc); } + pcre2_match_data_free(match_data); + match_data = NULL; } if (g->suffix_regexp) { - rc = pcre_exec(g->suffix_regexp, 0, work, work_len, 0, 0, ovector, OVECCOUNT); + match_data = pcre2_match_data_create_from_pattern(g->suffix_regexp, NULL); + if (!match_data) { + log_error("allocation error %s", strerror(errno)); + goto err; + } + rc = pcre2_match(g->suffix_regexp, (PCRE2_SPTR8)work, work_len, 0, 0, match_data, NULL); if (rc > 0) { + const PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); suffix = 1; suffix_offset = ovector[0]; suffix_len = ovector[1] - ovector[0]; + } else if (rc != PCRE2_ERROR_NOMATCH) { + log_error("compute_raw_from_trans: suffix matching error for input '%s': %d\n", level, rc); } + pcre2_match_data_free(match_data); + match_data = NULL; } /* anchors prefix ^, suffix $ */ @@ -1186,14 +1220,23 @@ compute_raw_from_trans(const char *level, domain_t *domain) { (g->suffixes && suffix)) && g->word_regexp) { char *s = work + prefix_offset + prefix_len; - int l = (suffix_len ? suffix_offset : work_len) - prefix_len - prefix_offset; - rc = pcre_exec(g->word_regexp, 0, s, l, 0, 0, ovector, OVECCOUNT); + PCRE2_SIZE len = (suffix_len ? suffix_offset : work_len) - prefix_len - prefix_offset; + match_data = pcre2_match_data_create_from_pattern(g->word_regexp, NULL); + if (!match_data) { + log_error("allocation error %s", strerror(errno)); + goto err; + } + rc = pcre2_match(g->word_regexp, (PCRE2_SPTR8)s, len, 0, 0, match_data, NULL); if (rc > 0) { - match = NULL; - pcre_get_substring(s, ovector, rc, 0, &match); - trim((char *)match, g->whitespace); + const PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); + match = strndup(s + ovector[0], ovector[1] - ovector[0]); + if (!match) { + log_error("allocation error %s", strerror(errno)); + goto err; + } + trim(match, g->whitespace); if (*match) { - char *p = triml((char *)match, g->whitespace); + char *p = triml(match, g->whitespace); while (p && *p) { int plen = strlen(p); unsigned int i; @@ -1230,9 +1273,13 @@ compute_raw_from_trans(const char *level, domain_t *domain) { memset(work + suffix_offset, '#', suffix_len); memset(s + ovector[0], '#', ovector[1] - ovector[0]); } - pcre_free((void *)match); + free(match); match = NULL; + } else if (rc != PCRE2_ERROR_NOMATCH) { + log_error("compute_raw_from_trans: word matching error for input '%s' for substring '%s': %d\n", level, s, rc); } + pcre2_match_data_free(match_data); + match_data = NULL; } /* YYY */ complete=1; @@ -1271,14 +1318,15 @@ err: mls_level_destroy(mraw); free(mraw); free(work); - pcre_free((void *)match); + free(match); ebitmap_destroy(&tmp); ebitmap_destroy(&set); ebitmap_destroy(&clear); + pcre2_match_data_free(match_data); return NULL; } -char * +static char * compute_trans_from_raw(const char *level, domain_t *domain) { #ifdef DEBUG diff --git a/mcstrans/src/mcstrans.h b/mcstrans/src/mcstrans.h index e5cda93b..0addb325 100644 --- a/mcstrans/src/mcstrans.h +++ b/mcstrans/src/mcstrans.h @@ -6,4 +6,3 @@ extern int init_translations(void); extern void finish_context_translations(void); extern int trans_context(const char *, char **); extern int untrans_context(const char *, char **); - diff --git a/mcstrans/src/mcstransd.c b/mcstrans/src/mcstransd.c index 59c152e7..536c0f32 100644 --- a/mcstrans/src/mcstransd.c +++ b/mcstrans/src/mcstransd.c @@ -16,6 +16,8 @@ #include <sys/types.h> #include <sys/uio.h> #include <sys/un.h> + +#include "mcscolor.h" #include "mcstrans.h" #ifdef UNUSED @@ -43,15 +45,6 @@ #define log_debug(fmt, ...) do {} while (0) #endif -extern int init_translations(void); -extern void finish_context_translations(void); -extern int trans_context(const char *, char **); -extern int untrans_context(const char *, char **); - -extern int init_colors(void); -extern void finish_context_colors(void); -extern int raw_color(const char *, char **); - #define SETRANSD_PATHNAME "/sbin/mcstransd" /* name of program (for error messages) */ @@ -514,7 +507,7 @@ initialize(void) } -void dropprivs(void) +static void dropprivs(void) { cap_t new_caps; diff --git a/mcstrans/utils/Makefile b/mcstrans/utils/Makefile index 9dfe7723..a48f4e72 100644 --- a/mcstrans/utils/Makefile +++ b/mcstrans/utils/Makefile @@ -14,13 +14,13 @@ endif all: $(TARGETS) transcon: transcon.o ../src/mcstrans.o ../src/mls_level.o $(LIBSEPOLA) - $(CC) $(LDFLAGS) -o $@ $^ -lpcre -lselinux $(LDLIBS_LIBSEPOLA) + $(CC) $(LDFLAGS) -o $@ $^ $(PCRE_LDLIBS) -lselinux $(LDLIBS_LIBSEPOLA) untranscon: untranscon.o ../src/mcstrans.o ../src/mls_level.o $(LIBSEPOLA) - $(CC) $(LDFLAGS) -o $@ $^ -lpcre -lselinux $(LDLIBS_LIBSEPOLA) + $(CC) $(LDFLAGS) -o $@ $^ $(PCRE_LDLIBS) -lselinux $(LDLIBS_LIBSEPOLA) %.o: %.c - $(CC) $(CFLAGS) -D_GNU_SOURCE -I../src -fPIE -c -o $@ $< + $(CC) $(CFLAGS) $(PCRE_CFLAGS) -D_GNU_SOURCE -I../src -fPIE -c -o $@ $< install: all -mkdir -p $(DESTDIR)$(SBINDIR) diff --git a/policycoreutils/man/man5/selinux_config.5 b/policycoreutils/man/man5/selinux_config.5 index 58b42a0e..f391befb 100644 --- a/policycoreutils/man/man5/selinux_config.5 +++ b/policycoreutils/man/man5/selinux_config.5 @@ -32,7 +32,7 @@ The \fIconfig\fR file supports the following parameters: .br \fBSELINUXTYPE = \fIpolicy_name\fR .br -\fBREQUIREUSERS = \fI0\fR | \fI1\fR +\fBREQUIRESEUSERS = \fI0\fR | \fI1\fR .br \fBAUTORELABEL = \fI0\fR | \fI1\fR .RE diff --git a/policycoreutils/man/ru/man5/selinux_config.5 b/policycoreutils/man/ru/man5/selinux_config.5 index 40039e57..8c0db9ae 100644 --- a/policycoreutils/man/ru/man5/selinux_config.5 +++ b/policycoreutils/man/ru/man5/selinux_config.5 @@ -34,7 +34,7 @@ config \- файл конфигурации подсистемы SELinux. .br \fBSELINUXTYPE = \fIpolicy_name\fR .br -\fBREQUIREUSERS = \fI0\fR | \fI1\fR +\fBREQUIRESEUSERS = \fI0\fR | \fI1\fR .br \fBAUTORELABEL = \fI0\fR | \fI1\fR .RE diff --git a/policycoreutils/newrole/Makefile b/policycoreutils/newrole/Makefile index 4dedb7dd..b3ccf671 100644 --- a/policycoreutils/newrole/Makefile +++ b/policycoreutils/newrole/Makefile @@ -91,3 +91,16 @@ indent: relabel: install /sbin/restorecon $(DESTDIR)$(BINDIR)/newrole + +test-build-options: + $(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=y NAMESPACE_PRIV=y clean newrole + $(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=y NAMESPACE_PRIV=n clean newrole + $(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=n NAMESPACE_PRIV=y clean newrole + $(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=n NAMESPACE_PRIV=n clean newrole + $(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=y NAMESPACE_PRIV=y clean newrole + $(MAKE) PAMH=y AUDITH=n AUDIT_LOG_PRIV=n NAMESPACE_PRIV=y clean newrole + $(MAKE) PAMH=y AUDITH=n AUDIT_LOG_PRIV=n NAMESPACE_PRIV=n clean newrole + $(MAKE) PAMH=n AUDITH=y AUDIT_LOG_PRIV=y NAMESPACE_PRIV=n clean newrole + $(MAKE) PAMH=n AUDITH=y AUDIT_LOG_PRIV=n NAMESPACE_PRIV=n clean newrole + $(MAKE) PAMH=n AUDITH=n AUDIT_LOG_PRIV=n NAMESPACE_PRIV=n clean newrole + $(MAKE) clean diff --git a/policycoreutils/newrole/hashtab.c b/policycoreutils/newrole/hashtab.c index bc502836..26d4f4c7 100644 --- a/policycoreutils/newrole/hashtab.c +++ b/policycoreutils/newrole/hashtab.c @@ -44,7 +44,7 @@ hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h, int hashtab_insert(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum) { - int hvalue; + unsigned int hvalue; hashtab_ptr_t prev, cur, newnode; if (!h) @@ -83,7 +83,7 @@ int hashtab_remove(hashtab_t h, hashtab_key_t key, void (*destroy) (hashtab_key_t k, hashtab_datum_t d, void *args), void *args) { - int hvalue; + unsigned int hvalue; hashtab_ptr_t cur, last; if (!h) @@ -115,7 +115,7 @@ int hashtab_remove(hashtab_t h, hashtab_key_t key, hashtab_datum_t hashtab_search(hashtab_t h, const_hashtab_key_t key) { - int hvalue; + unsigned int hvalue; hashtab_ptr_t cur; if (!h) @@ -160,8 +160,9 @@ int hashtab_map(hashtab_t h, int (*apply) (hashtab_key_t k, hashtab_datum_t d, void *args), void *args) { - unsigned int i, ret; + unsigned int i; hashtab_ptr_t cur; + int ret; if (!h) return HASHTAB_SUCCESS; diff --git a/policycoreutils/newrole/newrole.c b/policycoreutils/newrole/newrole.c index 31b51c5a..ae37d725 100644 --- a/policycoreutils/newrole/newrole.c +++ b/policycoreutils/newrole/newrole.c @@ -100,7 +100,6 @@ #endif #define DEFAULT_PATH "/usr/bin:/bin" -#define DEFAULT_CONTEXT_SIZE 255 /* first guess at context size */ extern char **environ; @@ -115,7 +114,7 @@ extern char **environ; * * Returns malloc'd memory */ -static char *build_new_range(char *newlevel, const char *range) +static char *build_new_range(const char *newlevel, const char *range) { char *newrangep = NULL; const char *tmpptr; @@ -166,7 +165,7 @@ static char *build_new_range(char *newlevel, const char *range) #include <security/pam_appl.h> /* for PAM functions */ #include <security/pam_misc.h> /* for misc_conv PAM utility function */ -const char *service_name = "newrole"; +static const char *service_name = "newrole"; /* authenticate_via_pam() * @@ -182,7 +181,7 @@ const char *service_name = "newrole"; * program. This is the only function in this program that makes PAM * calls. */ -int authenticate_via_pam(const char *ttyn, pam_handle_t * pam_handle) +static int authenticate_via_pam(const char *ttyn, pam_handle_t * pam_handle) { int result = 0; /* set to 0 (not authenticated) by default */ @@ -230,14 +229,13 @@ static int free_hashtab_entry(hashtab_key_t key, hashtab_datum_t d, static unsigned int reqsymhash(hashtab_t h, const_hashtab_key_t key) { - char *p, *keyp; + const char *p; size_t size; unsigned int val; val = 0; - keyp = (char *)key; - size = strlen(keyp); - for (p = keyp; ((size_t) (p - keyp)) < size; p++) + size = strlen(key); + for (p = key; ((size_t) (p - key)) < size; p++) val = (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p); return val & (h->size - 1); @@ -335,6 +333,14 @@ static int read_pam_config(void) #define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */ +static void memzero(void *ptr, size_t size) +{ + volatile unsigned char * volatile p = ptr; + while (size--) { + *p++ = '\0'; + } +} + /* authenticate_via_shadow_passwd() * * in: uname - the calling user's user name @@ -348,11 +354,12 @@ static int read_pam_config(void) * This function uses the shadow passwd file to thenticate the user running * this program. */ -int authenticate_via_shadow_passwd(const char *uname) +static int authenticate_via_shadow_passwd(const char *uname) { struct spwd *p_shadow_line; char *unencrypted_password_s; char *encrypted_password_s; + int ret; setspent(); p_shadow_line = getspnam(uname); @@ -370,10 +377,18 @@ int authenticate_via_shadow_passwd(const char *uname) } /* Use crypt() to encrypt user's input password. */ + errno = 0; encrypted_password_s = crypt(unencrypted_password_s, p_shadow_line->sp_pwdp); - memset(unencrypted_password_s, 0, strlen(unencrypted_password_s)); - return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp)); + memzero(unencrypted_password_s, strlen(unencrypted_password_s)); + if (errno || !encrypted_password_s) { + fprintf(stderr, _("Cannot encrypt password.\n")); + return 0; + } + + ret = !strcmp(encrypted_password_s, p_shadow_line->sp_pwdp); + memzero(encrypted_password_s, strlen(encrypted_password_s)); + return ret; } #endif /* if/else USE_PAM */ @@ -623,7 +638,7 @@ static inline int drop_capabilities(__attribute__ ((__unused__)) int full) * This function will set the uid values to be that of caller's uid, and * will drop any privilege which may have been raised. */ -static int transition_to_caller_uid() +static int transition_to_caller_uid(void) { uid_t uid = getuid(); @@ -850,7 +865,6 @@ static int parse_command_line_arguments(int argc, char **argv, char *ttyn, case 'V': printf("newrole: %s version %s\n", PACKAGE, VERSION); exit(0); - break; case 'p': *preserve_environment = 1; break; diff --git a/policycoreutils/run_init/open_init_pty.c b/policycoreutils/run_init/open_init_pty.c index 150cb45e..19101c50 100644 --- a/policycoreutils/run_init/open_init_pty.c +++ b/policycoreutils/run_init/open_init_pty.c @@ -244,7 +244,7 @@ int main(int argc, char *argv[]) rb_init(&inbuf, inbuf_mem, sizeof(inbuf_mem)); rb_init(&outbuf, outbuf_mem, sizeof(outbuf_mem)); - if (argc == 1) { + if (argc < 2) { printf("usage: %s PROGRAM [ARGS]...\n", argv[0]); exit(1); } diff --git a/policycoreutils/run_init/run_init.c b/policycoreutils/run_init/run_init.c index 545490a2..ce499781 100644 --- a/policycoreutils/run_init/run_init.c +++ b/policycoreutils/run_init/run_init.c @@ -86,8 +86,6 @@ /* The file containing the context to run * the scripts under. */ -int authenticate_via_pam(const struct passwd *); - /* authenticate_via_pam() * * in: p_passwd_line - struct containing data from our user's line in @@ -104,7 +102,7 @@ int authenticate_via_pam(const struct passwd *); * */ -int authenticate_via_pam(const struct passwd *p_passwd_line) +static int authenticate_via_pam(const struct passwd *p_passwd_line) { int result = 0; /* our result, set to 0 (not authenticated) by default */ @@ -169,8 +167,6 @@ int authenticate_via_pam(const struct passwd *p_passwd_line) #define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */ -int authenticate_via_shadow_passwd(const struct passwd *); - /* authenticate_via_shadow_passwd() * * in: p_passwd_line - struct containing data from our user's line in @@ -187,7 +183,7 @@ int authenticate_via_shadow_passwd(const struct passwd *); * */ -int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line) +static int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line) { struct spwd *p_shadow_line; /* struct derived from shadow passwd file line */ @@ -238,7 +234,7 @@ int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line) * return: 0 When success * -1 When failure */ -int authenticate_user(void) +static int authenticate_user(void) { #define INITLEN 255 @@ -303,7 +299,7 @@ int authenticate_user(void) * out: The CONTEXT associated with the context. * return: 0 on success, -1 on failure. */ -int get_init_context(char **context) +static int get_init_context(char **context) { FILE *fp; diff --git a/policycoreutils/scripts/fixfiles b/policycoreutils/scripts/fixfiles index 6fb12e04..7df4303a 100755 --- a/policycoreutils/scripts/fixfiles +++ b/policycoreutils/scripts/fixfiles @@ -109,6 +109,7 @@ fullFlag=0 BOOTTIME="" VERBOSE="-p" FORCEFLAG="" +THREADS="" RPMFILES="" PREFC="" RESTORE_MODE="" @@ -152,7 +153,7 @@ newer() { shift LogReadOnly for m in `echo $FILESYSTEMSRW`; do - find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} $* -i -0 -f - + find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} ${THREADS} $* -i -0 -f - done; } @@ -196,7 +197,7 @@ if [ -f ${PREFC} -a -x /usr/bin/diff ]; then esac; \ fi; \ done | \ - ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -i -R -f -; \ + ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f -; \ rm -f ${TEMPFILE} ${PREFCTEMPFILE} fi } @@ -234,11 +235,11 @@ LogExcluded case "$RESTORE_MODE" in RPMFILES) for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do - rpmlist $i | ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -i -R -f - + rpmlist $i | ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f - done ;; FILEPATH) - ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -R -- "$FILEPATH" + ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -R -- "$FILEPATH" ;; *) if [ -n "${FILESYSTEMSRW}" ]; then @@ -246,7 +247,7 @@ case "$RESTORE_MODE" in echo "${OPTION}ing `echo ${FILESYSTEMSRW}`" if [ -z "$BIND_MOUNT_FILESYSTEMS" ]; then - ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${FC} ${FILESYSTEMSRW} + ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${THREADS} ${FC} ${FILESYSTEMSRW} else # we bind mount so we can fix the labels of files that have already been # mounted over @@ -256,7 +257,7 @@ case "$RESTORE_MODE" in mkdir -p "${TMP_MOUNT}${m}" || exit 1 mount --bind "${m}" "${TMP_MOUNT}${m}" || exit 1 - ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${FC} -r "${TMP_MOUNT}" "${TMP_MOUNT}${m}" + ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -q ${FC} -r "${TMP_MOUNT}" "${TMP_MOUNT}${m}" umount "${TMP_MOUNT}${m}" || exit 1 rm -rf "${TMP_MOUNT}" || echo "Error cleaning up." done; @@ -329,8 +330,9 @@ case "$1" in fi > /.autorelabel || exit $? [ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel - [ -z "$BOOTTIME" ] || echo -N $BOOTTIME >> /.autorelabel - [ -z "$BIND_MOUNT_FILESYSTEMS" ] || echo "-M" >> /.autorelabel + [ -z "$BOOTTIME" ] || echo -n "-N $BOOTTIME " >> /.autorelabel + [ -z "$BIND_MOUNT_FILESYSTEMS" ] || echo -n "-M " >> /.autorelabel + [ -z "$THREADS" ] || echo -n "$THREADS " >> /.autorelabel # Force full relabel if SELinux is not enabled selinuxenabled || echo -F > /.autorelabel echo "System will relabel on next boot" @@ -342,17 +344,17 @@ esac } usage() { echo $""" -Usage: $0 [-v] [-F] [-M] [-f] relabel +Usage: $0 [-v] [-F] [-M] [-f] [-T nthreads] relabel or -Usage: $0 [-v] [-F] [-B | -N time ] { check | restore | verify } +Usage: $0 [-v] [-F] [-B | -N time ] [-T nthreads] { check | restore | verify } or -Usage: $0 [-v] [-F] { check | restore | verify } dir/file ... +Usage: $0 [-v] [-F] [-T nthreads] { check | restore | verify } dir/file ... or -Usage: $0 [-v] [-F] -R rpmpackage[,rpmpackage...] { check | restore | verify } +Usage: $0 [-v] [-F] [-T nthreads] -R rpmpackage[,rpmpackage...] { check | restore | verify } or -Usage: $0 [-v] [-F] -C PREVIOUS_FILECONTEXT { check | restore | verify } +Usage: $0 [-v] [-F] [-T nthreads] -C PREVIOUS_FILECONTEXT { check | restore | verify } or -Usage: $0 [-F] [-M] [-B] onboot +Usage: $0 [-F] [-M] [-B] [-T nthreads] onboot """ } @@ -371,7 +373,7 @@ set_restore_mode() { } # See how we were called. -while getopts "N:BC:FfR:l:vM" i; do +while getopts "N:BC:FfR:l:vMT:" i; do case "$i" in B) BOOTTIME=`/bin/who -b | awk '{print $3}'` @@ -406,6 +408,9 @@ while getopts "N:BC:FfR:l:vM" i; do f) fullFlag=1 ;; + T) + THREADS="-T $OPTARG" + ;; *) usage exit 1 diff --git a/policycoreutils/scripts/fixfiles.8 b/policycoreutils/scripts/fixfiles.8 index c4e894e5..9a317d91 100644 --- a/policycoreutils/scripts/fixfiles.8 +++ b/policycoreutils/scripts/fixfiles.8 @@ -6,22 +6,22 @@ fixfiles \- fix file SELinux security contexts. .na .B fixfiles -.I [\-v] [\-F] [-M] [\-f] relabel +.I [\-v] [\-F] [-M] [\-f] [\-T nthreads] relabel .B fixfiles -.I [\-v] [\-F] { check | restore | verify } dir/file ... +.I [\-v] [\-F] [\-T nthreads] { check | restore | verify } dir/file ... .B fixfiles -.I [\-v] [\-F] [\-B | \-N time ] { check | restore | verify } +.I [\-v] [\-F] [\-B | \-N time ] [\-T nthreads] { check | restore | verify } .B fixfiles -.I [\-v] [\-F] \-R rpmpackagename[,rpmpackagename...] { check | restore | verify } +.I [\-v] [\-F] [\-T nthreads] \-R rpmpackagename[,rpmpackagename...] { check | restore | verify } .B fixfiles -.I [\-v] [\-F] \-C PREVIOUS_FILECONTEXT { check | restore | verify } +.I [\-v] [\-F] [\-T nthreads] \-C PREVIOUS_FILECONTEXT { check | restore | verify } .B fixfiles -.I [-F] [-M] [-B] onboot +.I [-F] [-M] [-B] [\-T nthreads] onboot .ad @@ -76,6 +76,11 @@ Bind mount filesystems before relabeling them, this allows fixing the context of .B -v Modify verbosity from progress to verbose. (Run restorecon with \-v instead of \-p) +.TP +.B \-T nthreads +Use parallel relabeling, see +.B setfiles(8) + .SH "ARGUMENTS" One of: .TP diff --git a/policycoreutils/secon/secon.c b/policycoreutils/secon/secon.c index a0957d09..d624fa13 100644 --- a/policycoreutils/secon/secon.c +++ b/policycoreutils/secon/secon.c @@ -333,6 +333,9 @@ static void cmd_line(int argc, char *argv[]) opts->from_type = OPTS_FROM_CUR; if (opts->from_type == OPTS_FROM_ARG) { + if (!argv[0]) + errx(EXIT_FAILURE, "No argument given"); + opts->f.arg = argv[0]; if (xstreq(argv[0], "-")) diff --git a/policycoreutils/semodule/semodule.8 b/policycoreutils/semodule/semodule.8 index 18d4f708..d1735d21 100644 --- a/policycoreutils/semodule/semodule.8 +++ b/policycoreutils/semodule/semodule.8 @@ -23,6 +23,13 @@ force a reload of policy .B \-B, \-\-build force a rebuild of policy (also reloads unless \-n is used) .TP +.B \-\-rebuild-if-modules-changed +Force a rebuild of the policy if any changes to module content are detected +(by comparing with checksum from the last transaction). One can use this +instead of \-B to ensure that any changes to the module store done by an +external tool (e.g. a package manager) are applied, while automatically +skipping the rebuild if there are no new changes. +.TP .B \-D, \-\-disable_dontaudit Temporarily remove dontaudits from policy. Reverts whenever policy is rebuilt .TP @@ -95,6 +102,9 @@ only modules listed in \-\-extract after this option. .B \-H,\-\-hll Extract module as an HLL file. This only affects the \-\-extract option and only modules listed in \-\-extract after this option. +.TP +.B \-m,\-\-checksum +Add SHA256 checksum of modules to the list output. .SH EXAMPLE .nf @@ -130,6 +140,9 @@ $ semodule \-B \-S "/tmp/var/lib/selinux" # Write the HLL version of puppet and the CIL version of wireshark # modules at priority 400 to the current working directory $ semodule \-X 400 \-\-hll \-E puppet \-\-cil \-E wireshark +# Check whether a module in "localmodule.pp" file is same as installed module "localmodule" +$ /usr/libexec/selinux/hll/pp localmodule.pp | sha256sum +$ semodule -l -m | grep localmodule .fi .SH SEE ALSO diff --git a/policycoreutils/semodule/semodule.c b/policycoreutils/semodule/semodule.c index c815f015..1ed8e690 100644 --- a/policycoreutils/semodule/semodule.c +++ b/policycoreutils/semodule/semodule.c @@ -47,6 +47,7 @@ static int verbose; static int reload; static int no_reload; static int build; +static int check_ext_changes; static int disable_dontaudit; static int preserve_tunables; static int ignore_module_cache; @@ -57,6 +58,7 @@ static semanage_handle_t *sh = NULL; static char *store; static char *store_root; int extract_cil = 0; +static int checksum = 0; extern char *optarg; extern int optind; @@ -147,6 +149,10 @@ static void usage(char *progname) printf(" -S,--store-path use an alternate path for the policy store root\n"); printf(" -c, --cil extract module as cil. This only affects module extraction.\n"); printf(" -H, --hll extract module as hll. This only affects module extraction.\n"); + printf(" -m, --checksum print module checksum (SHA256).\n"); + printf(" --rebuild-if-modules-changed\n" + " force policy rebuild if module content changed since\n" + " last rebuild (based on checksum)\n"); } /* Sets the global mode variable to new_mode, but only if no other @@ -178,6 +184,7 @@ static void set_mode(enum client_modes new_mode, char *arg) static void parse_command_line(int argc, char **argv) { static struct option opts[] = { + {"rebuild-if-modules-changed", 0, NULL, '\0'}, {"store", required_argument, NULL, 's'}, {"base", required_argument, NULL, 'b'}, {"help", 0, NULL, 'h'}, @@ -200,19 +207,31 @@ static void parse_command_line(int argc, char **argv) {"disable", required_argument, NULL, 'd'}, {"path", required_argument, NULL, 'p'}, {"store-path", required_argument, NULL, 'S'}, + {"checksum", 0, NULL, 'm'}, {NULL, 0, NULL, 0} }; int extract_selected = 0; int cil_hll_set = 0; - int i; + int i, longind; verbose = 0; reload = 0; no_reload = 0; + check_ext_changes = 0; priority = 400; while ((i = - getopt_long(argc, argv, "s:b:hi:l::vr:u:RnNBDCPX:e:d:p:S:E:cH", opts, - NULL)) != -1) { + getopt_long(argc, argv, "s:b:hi:l::vr:u:RnNBDCPX:e:d:p:S:E:cHm", + opts, &longind)) != -1) { switch (i) { + case '\0': + switch(longind) { + case 0: /* --rebuild-if-modules-changed */ + check_ext_changes = 1; + break; + default: + usage(argv[0]); + exit(1); + } + break; case 'b': fprintf(stderr, "The --base option is deprecated. Use --install instead.\n"); set_mode(INSTALL_M, optarg); @@ -287,6 +306,9 @@ static void parse_command_line(int argc, char **argv) case 'd': set_mode(DISABLE_M, optarg); break; + case 'm': + checksum = 1; + break; case '?': default:{ usage(argv[0]); @@ -294,13 +316,13 @@ static void parse_command_line(int argc, char **argv) } } } - if ((build || reload) && num_commands) { + if ((build || reload || check_ext_changes) && num_commands) { fprintf(stderr, "build or reload should not be used with other commands\n"); usage(argv[0]); exit(1); } - if (num_commands == 0 && reload == 0 && build == 0) { + if (num_commands == 0 && reload == 0 && build == 0 && check_ext_changes == 0) { fprintf(stderr, "At least one mode must be specified.\n"); usage(argv[0]); exit(1); @@ -338,6 +360,42 @@ static void parse_command_line(int argc, char **argv) } } +/* Get module checksum */ +static char *hash_module_data(const char *module_name, const int prio) { + semanage_module_key_t *modkey = NULL; + char *hash_str = NULL; + void *hash = NULL; + size_t hash_len = 0; + int result; + + result = semanage_module_key_create(sh, &modkey); + if (result != 0) { + goto cleanup; + } + + result = semanage_module_key_set_name(sh, modkey, module_name); + if (result != 0) { + goto cleanup; + } + + result = semanage_module_key_set_priority(sh, modkey, prio); + if (result != 0) { + goto cleanup; + } + + result = semanage_module_compute_checksum(sh, modkey, 1, &hash_str, + &hash_len); + if (result != 0) { + goto cleanup; + } + +cleanup: + free(hash); + semanage_module_key_destroy(sh, modkey); + free(modkey); + return hash_str; +} + int main(int argc, char *argv[]) { int i, commit = 0; @@ -353,7 +411,7 @@ int main(int argc, char *argv[]) cil_set_log_level(CIL_ERR + verbose); - if (build) + if (build || check_ext_changes) commit = 1; sh = semanage_handle_create(); @@ -392,7 +450,7 @@ int main(int argc, char *argv[]) } } - if (build) { + if (build || check_ext_changes) { if ((result = semanage_begin_transaction(sh)) < 0) { fprintf(stderr, "%s: Could not begin transaction: %s\n", argv[0], errno ? strerror(errno) : ""); @@ -546,6 +604,8 @@ cleanup_extract: int modinfos_len = 0; semanage_module_info_t *m = NULL; int j = 0; + char *module_checksum = NULL; + uint16_t pri = 0; if (verbose) { printf @@ -570,7 +630,18 @@ cleanup_extract: result = semanage_module_info_get_name(sh, m, &name); if (result != 0) goto cleanup_list; - printf("%s\n", name); + result = semanage_module_info_get_priority(sh, m, &pri); + if (result != 0) goto cleanup_list; + + printf("%s", name); + if (checksum) { + module_checksum = hash_module_data(name, pri); + if (module_checksum) { + printf(" %s", module_checksum); + free(module_checksum); + } + } + printf("\n"); } } else if (strcmp(mode_arg, "full") == 0) { @@ -585,12 +656,16 @@ cleanup_extract: } /* calculate column widths */ - size_t column[4] = { 0, 0, 0, 0 }; + size_t column[5] = { 0, 0, 0, 0, 0 }; /* fixed width columns */ column[0] = sizeof("000") - 1; column[3] = sizeof("disabled") - 1; + result = semanage_module_compute_checksum(sh, NULL, 0, NULL, + &column[4]); + if (result != 0) goto cleanup_list; + /* variable width columns */ const char *tmp = NULL; size_t size; @@ -607,12 +682,11 @@ cleanup_extract: if (result != 0) goto cleanup_list; size = strlen(tmp); - if (size > column[3]) column[3] = size; + if (size > column[2]) column[2] = size; } /* print out each module */ for (j = 0; j < modinfos_len; j++) { - uint16_t pri = 0; const char *name = NULL; int enabled = 0; const char *lang_ext = NULL; @@ -631,11 +705,20 @@ cleanup_extract: result = semanage_module_info_get_lang_ext(sh, m, &lang_ext); if (result != 0) goto cleanup_list; - printf("%0*u %-*s %-*s %-*s\n", + printf("%0*u %-*s %-*s %-*s", (int)column[0], pri, (int)column[1], name, (int)column[2], lang_ext, (int)column[3], enabled ? "" : "disabled"); + if (checksum) { + module_checksum = hash_module_data(name, pri); + if (module_checksum) { + printf(" %-*s", (int)column[4], module_checksum); + free(module_checksum); + } + } + printf("\n"); + } } else { @@ -740,6 +823,8 @@ cleanup_disable: semanage_set_reload(sh, 0); if (build) semanage_set_rebuild(sh, 1); + if (check_ext_changes) + semanage_set_check_ext_changes(sh, 1); if (disable_dontaudit) semanage_set_disable_dontaudit(sh, 1); else if (build) diff --git a/policycoreutils/sestatus/sestatus.c b/policycoreutils/sestatus/sestatus.c index ceee0d52..7dcc9944 100644 --- a/policycoreutils/sestatus/sestatus.c +++ b/policycoreutils/sestatus/sestatus.c @@ -35,7 +35,7 @@ static unsigned int COL = 32; extern char *selinux_mnt; -int cmp_cmdline(const char *command, int pid) +static int cmp_cmdline(const char *command, int pid) { char buf[BUFSIZE]; @@ -59,7 +59,7 @@ int cmp_cmdline(const char *command, int pid) return 0; } -int pidof(const char *command) +static int pidof(const char *command) { /* inspired by killall5.c from psmisc */ char stackpath[PATH_MAX + 1], *p; @@ -92,7 +92,7 @@ int pidof(const char *command) return ret; } -void load_checks(char *pc[], int *npc, char *fc[], int *nfc) +static void load_checks(char *pc[], int *npc, char *fc[], int *nfc) { FILE *fp = fopen(CONF, "r"); @@ -168,11 +168,9 @@ void load_checks(char *pc[], int *npc, char *fc[], int *nfc) return; } -void printf_tab(const char *outp) +static void printf_tab(const char *outp) { - char buf[20]; - snprintf(buf, sizeof(buf), "%%-%us", COL); - printf(buf, outp); + printf("%-*s", COL, outp); } diff --git a/policycoreutils/setfiles/Makefile b/policycoreutils/setfiles/Makefile index 63d81850..d7670a8f 100644 --- a/policycoreutils/setfiles/Makefile +++ b/policycoreutils/setfiles/Makefile @@ -6,7 +6,7 @@ MANDIR = $(PREFIX)/share/man AUDITH ?= $(shell test -f /usr/include/libaudit.h && echo y) CFLAGS ?= -g -Werror -Wall -W -override LDLIBS += -lselinux -lsepol +override LDLIBS += -lselinux -lsepol -lpthread ifeq ($(AUDITH), y) override CFLAGS += -DUSE_AUDIT diff --git a/policycoreutils/setfiles/restore.c b/policycoreutils/setfiles/restore.c index 9d688c60..e9ae33ad 100644 --- a/policycoreutils/setfiles/restore.c +++ b/policycoreutils/setfiles/restore.c @@ -29,7 +29,7 @@ void restore_init(struct restore_opts *opts) opts->hnd = selabel_open(SELABEL_CTX_FILE, selinux_opts, 3); if (!opts->hnd) { - perror(opts->selabel_opt_path); + perror(opts->selabel_opt_path ? opts->selabel_opt_path : selinux_file_context_path()); exit(1); } @@ -72,7 +72,7 @@ void restore_finish(void) } } -int process_glob(char *name, struct restore_opts *opts) +int process_glob(char *name, struct restore_opts *opts, size_t nthreads) { glob_t globbuf; size_t i = 0; @@ -91,8 +91,9 @@ int process_glob(char *name, struct restore_opts *opts) continue; if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) continue; - rc = selinux_restorecon(globbuf.gl_pathv[i], - opts->restorecon_flags); + rc = selinux_restorecon_parallel(globbuf.gl_pathv[i], + opts->restorecon_flags, + nthreads); if (rc < 0) errors = rc; } diff --git a/policycoreutils/setfiles/restore.h b/policycoreutils/setfiles/restore.h index ac6ad680..bb35a1db 100644 --- a/policycoreutils/setfiles/restore.h +++ b/policycoreutils/setfiles/restore.h @@ -49,7 +49,7 @@ struct restore_opts { void restore_init(struct restore_opts *opts); void restore_finish(void); void add_exclude(const char *directory); -int process_glob(char *name, struct restore_opts *opts); +int process_glob(char *name, struct restore_opts *opts, size_t nthreads); extern char **exclude_list; #endif diff --git a/policycoreutils/setfiles/restorecon.8 b/policycoreutils/setfiles/restorecon.8 index 668486f6..e07db2c8 100644 --- a/policycoreutils/setfiles/restorecon.8 +++ b/policycoreutils/setfiles/restorecon.8 @@ -33,6 +33,8 @@ restorecon \- restore file(s) default SELinux security contexts. .RB [ \-W ] .RB [ \-I | \-D ] .RB [ \-x ] +.RB [ \-T +.IR nthreads ] .SH "DESCRIPTION" This manual page describes the @@ -160,6 +162,13 @@ prevent .B restorecon from crossing file system boundaries. .TP +.BI \-T \ nthreads +use up to +.I nthreads +threads. Specify 0 to create as many threads as there are available +CPU cores; 1 to use only a single thread (default); or any positive +number to use the given number of threads (if possible). +.TP .SH "ARGUMENTS" .IR pathname \ ... The pathname for the file(s) to be relabeled. diff --git a/policycoreutils/setfiles/setfiles.8 b/policycoreutils/setfiles/setfiles.8 index 4d28bc9a..15f939d1 100644 --- a/policycoreutils/setfiles/setfiles.8 +++ b/policycoreutils/setfiles/setfiles.8 @@ -19,6 +19,8 @@ setfiles \- set SELinux file security contexts. .RB [ \-W ] .RB [ \-F ] .RB [ \-I | \-D ] +.RB [ \-T +.IR nthreads ] .I spec_file .IR pathname \ ... @@ -161,6 +163,13 @@ quote marks or backslashes. The option of GNU .B find produces input suitable for this mode. +.TP +.BI \-T \ nthreads +use up to +.I nthreads +threads. Specify 0 to create as many threads as there are available +CPU cores; 1 to use only a single thread (default); or any positive +number to use the given number of threads (if possible). .SH "ARGUMENTS" .TP diff --git a/policycoreutils/setfiles/setfiles.c b/policycoreutils/setfiles/setfiles.c index f018d161..ab7016ac 100644 --- a/policycoreutils/setfiles/setfiles.c +++ b/policycoreutils/setfiles/setfiles.c @@ -1,4 +1,5 @@ #include "restore.h" +#include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <stdio_ext.h> @@ -34,20 +35,20 @@ static __attribute__((__noreturn__)) void usage(const char *const name) { if (iamrestorecon) { fprintf(stderr, - "usage: %s [-iIDFmnprRv0x] [-e excludedir] pathname...\n" - "usage: %s [-iIDFmnprRv0x] [-e excludedir] -f filename\n", + "usage: %s [-iIDFmnprRv0xT] [-e excludedir] pathname...\n" + "usage: %s [-iIDFmnprRv0xT] [-e excludedir] -f filename\n", name, name); } else { fprintf(stderr, - "usage: %s [-diIDlmnpqvEFW] [-e excludedir] [-r alt_root_path] [-c policyfile] spec_file pathname...\n" - "usage: %s [-diIDlmnpqvEFW] [-e excludedir] [-r alt_root_path] [-c policyfile] spec_file -f filename\n" - "usage: %s -s [-diIDlmnpqvFW] spec_file\n", + "usage: %s [-diIDlmnpqvEFWT] [-e excludedir] [-r alt_root_path] [-c policyfile] spec_file pathname...\n" + "usage: %s [-diIDlmnpqvEFWT] [-e excludedir] [-r alt_root_path] [-c policyfile] spec_file -f filename\n" + "usage: %s -s [-diIDlmnpqvFWT] spec_file\n", name, name, name); } exit(-1); } -void set_rootpath(const char *arg) +static void set_rootpath(const char *arg) { if (strlen(arg) == 1 && strncmp(arg, "/", 1) == 0) { fprintf(stderr, "%s: invalid alt_rootpath: %s\n", @@ -64,7 +65,7 @@ void set_rootpath(const char *arg) } } -int canoncon(char **contextp) +static int canoncon(char **contextp) { char *context = *contextp, *tmpcon; int rc = 0; @@ -144,12 +145,12 @@ int main(int argc, char **argv) int opt, i = 0; const char *input_filename = NULL; int use_input_file = 0; - char *buf = NULL; - size_t buf_len; + char *buf = NULL, *endptr; + size_t buf_len, nthreads = 1; const char *base; int errors = 0; - const char *ropts = "e:f:hiIDlmno:pqrsvFRW0x"; - const char *sopts = "c:de:f:hiIDlmno:pqr:svEFR:W0"; + const char *ropts = "e:f:hiIDlmno:pqrsvFRW0xT:"; + const char *sopts = "c:de:f:hiIDlmno:pqr:svEFR:W0T:"; const char *opts; union selinux_callback cb; @@ -162,6 +163,10 @@ int main(int argc, char **argv) policyfile = NULL; r_opts.abort_on_error = 0; + if (!argv[0]) { + fprintf(stderr, "Called without required program name!\n"); + exit(-1); + } r_opts.progname = strdup(argv[0]); if (!r_opts.progname) { fprintf(stderr, "%s: Out of memory!\n", argv[0]); @@ -370,6 +375,11 @@ int main(int argc, char **argv) usage(argv[0]); } break; + case 'T': + nthreads = strtoull(optarg, &endptr, 10); + if (*optarg == '\0' || *endptr != '\0') + usage(argv[0]); + break; case 'h': case '?': usage(argv[0]); @@ -417,7 +427,7 @@ int main(int argc, char **argv) altpath = argv[optind]; optind++; - } else if (argc == 1) + } else if (argc < 2) usage(argv[0]); /* Set selabel_open options. */ @@ -448,13 +458,13 @@ int main(int argc, char **argv) buf[len - 1] = 0; if (!strcmp(buf, "/")) r_opts.mass_relabel = SELINUX_RESTORECON_MASS_RELABEL; - errors |= process_glob(buf, &r_opts) < 0; + errors |= process_glob(buf, &r_opts, nthreads) < 0; } if (strcmp(input_filename, "-") != 0) fclose(f); } else { for (i = optind; i < argc; i++) - errors |= process_glob(argv[i], &r_opts) < 0; + errors |= process_glob(argv[i], &r_opts, nthreads) < 0; } maybe_audit_mass_relabel(r_opts.mass_relabel, errors); diff --git a/python/audit2allow/sepolgen-ifgen-attr-helper.c b/python/audit2allow/sepolgen-ifgen-attr-helper.c index f010c958..6f3ba962 100644 --- a/python/audit2allow/sepolgen-ifgen-attr-helper.c +++ b/python/audit2allow/sepolgen-ifgen-attr-helper.c @@ -56,7 +56,7 @@ static int perm_name(hashtab_key_t key, hashtab_datum_t datum, void *data) return 0; } -int render_access_mask(uint32_t av, avtab_key_t *key, policydb_t *policydbp, +static int render_access_mask(uint32_t av, avtab_key_t *key, policydb_t *policydbp, FILE *fp) { struct val_to_name v; @@ -111,7 +111,7 @@ struct callback_data FILE *fp; }; -int output_avrule(avtab_key_t *key, avtab_datum_t *datum, void *args) +static int output_avrule(avtab_key_t *key, avtab_datum_t *datum, void *args) { struct callback_data *cb_data = (struct callback_data *)args; @@ -217,7 +217,7 @@ static policydb_t *load_policy(const char *filename) } -void usage(char *progname) +static void usage(char *progname) { printf("usage: %s out_file [policy_file]\n", progname); } diff --git a/python/semanage/semanage-fcontext.8 b/python/semanage/semanage-fcontext.8 index 49635ba7..1ebf085f 100644 --- a/python/semanage/semanage-fcontext.8 +++ b/python/semanage/semanage-fcontext.8 @@ -3,7 +3,7 @@ semanage\-fcontext \- SELinux Policy Management file context tool .SH "SYNOPSIS" -.B semanage fcontext [\-h] [\-n] [\-N] [\-S STORE] [ \-\-add ( \-t TYPE \-f FTYPE \-r RANGE \-s SEUSER | \-e EQUAL ) FILE_SPEC ) | \-\-delete ( \-t TYPE \-f FTYPE | \-e EQUAL ) FILE_SPEC ) | \-\-deleteall | \-\-extract | \-\-list [\-C] | \-\-modify ( \-t TYPE \-f FTYPE \-r RANGE \-s SEUSER | \-e EQUAL ) FILE_SPEC ) ] +.B semanage fcontext [\-h] [\-n] [\-N] [\-S STORE] [ \-\-add ( \-t TYPE \-f FTYPE \-r RANGE \-s SEUSER | \-e EQUAL ) FILE_SPEC | \-\-delete ( \-t TYPE \-f FTYPE | \-e EQUAL ) FILE_SPEC | \-\-deleteall | \-\-extract | \-\-list [\-C] | \-\-modify ( \-t TYPE \-f FTYPE \-r RANGE \-s SEUSER | \-e EQUAL ) FILE_SPEC ] .SH "DESCRIPTION" semanage is used to configure certain elements of diff --git a/python/sepolgen/src/sepolgen/refparser.py b/python/sepolgen/src/sepolgen/refparser.py index e611637f..1bb90564 100644 --- a/python/sepolgen/src/sepolgen/refparser.py +++ b/python/sepolgen/src/sepolgen/refparser.py @@ -261,7 +261,7 @@ def t_IDENTIFIER(t): return t def t_FILENAME(t): - r'\"[a-zA-Z0-9_\-\+\.\$\*~ :]+\"' + r'\"[a-zA-Z0-9_\-\+\.\$\*~ :\[\]]+\"' # Handle any keywords t.type = reserved.get(t.value,'FILENAME') return t diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c index d626e98d..8917a0f9 100644 --- a/sandbox/seunshare.c +++ b/sandbox/seunshare.c @@ -89,7 +89,7 @@ static int drop_privs(uid_t uid) /** * If the user sends a siginto to seunshare, kill the child's session */ -void handler(int sig) { +static void handler(int sig) { if (child > 0) kill(-child,sig); } diff --git a/scripts/ci/fedora-test-runner.sh b/scripts/ci/fedora-test-runner.sh index f817499b..3ce2c3a6 100755 --- a/scripts/ci/fedora-test-runner.sh +++ b/scripts/ci/fedora-test-runner.sh @@ -36,7 +36,7 @@ dnf install -y \ libcap-devel \ libcap-ng-devel \ pam-devel \ - pcre-devel \ + pcre2-devel \ xmlto \ python3-devel \ ruby-devel \ diff --git a/scripts/oss-fuzz.sh b/scripts/oss-fuzz.sh index 16cc3c0a..72d275e8 100755 --- a/scripts/oss-fuzz.sh +++ b/scripts/oss-fuzz.sh @@ -32,7 +32,7 @@ SANITIZER=${SANITIZER:-address} flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link" export CC=${CC:-clang} -export CFLAGS=${CFLAGS:-$flags} +export CFLAGS="${CFLAGS:-$flags} -I$DESTDIR/usr/include -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" export CXX=${CXX:-clang++} export CXXFLAGS=${CXXFLAGS:-$flags} @@ -49,11 +49,24 @@ make -C libsepol clean # shellcheck disable=SC2016 make -C libsepol V=1 LD_SONAME_FLAGS='-soname,$(LIBSO),--version-script=$(LIBMAP)' -j"$(nproc)" install +## secilc fuzzer ## + # CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by # the compiler/linker so they shouldn't be quoted # shellcheck disable=SC2086 -$CC $CFLAGS -I"$DESTDIR/usr/include" -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c +$CC $CFLAGS -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c # shellcheck disable=SC2086 $CXX $CXXFLAGS $LIB_FUZZING_ENGINE secilc-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/secilc-fuzzer" zip -r "$OUT/secilc-fuzzer_seed_corpus.zip" secilc/test + +## binary policy fuzzer ## + +# CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by +# the compiler/linker so they shouldn't be quoted +# shellcheck disable=SC2086 +$CC $CFLAGS -c -o binpolicy-fuzzer.o libsepol/fuzz/binpolicy-fuzzer.c +# shellcheck disable=SC2086 +$CXX $CXXFLAGS $LIB_FUZZING_ENGINE binpolicy-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/binpolicy-fuzzer" + +zip -j "$OUT/binpolicy-fuzzer_seed_corpus.zip" libsepol/fuzz/policy.bin diff --git a/secilc/docs/cil_file_labeling_statements.md b/secilc/docs/cil_file_labeling_statements.md index ed7b7bf9..73f73885 100644 --- a/secilc/docs/cil_file_labeling_statements.md +++ b/secilc/docs/cil_file_labeling_statements.md @@ -36,11 +36,13 @@ Define entries for labeling files. The compiler will produce these entries in a <col width="44%" /> <col width="55%" /> </colgroup> -<tbody> +<thead> <tr class="odd"> <td align="left"><p><strong>keyword</strong></p></td> <td align="left"><p><strong>file_contexts entry</strong></p></td> </tr> +</thead> +<tbody> <tr class="even"> <td align="left"><p><code>file</code></p></td> <td align="left"><p><code>--</code></p></td> @@ -185,7 +187,7 @@ Used to allocate a security context to filesystems that cannot support any of th **Statement definition:** ```secil - (genfscon fsname path context_id) + (genfscon fsname path [file_type] context_id) ``` **Where:** @@ -209,6 +211,10 @@ Used to allocate a security context to filesystems that cannot support any of th <td align="left"><p>If <code>fsname</code> is <code>proc</code>, then the partial path (see examples). For all other types this must be ‘<code>/</code>’.</p></td> </tr> <tr class="even"> +<td align="left"><p><code>file_type</code></p></td> +<td align="left"><p>Optional keyword representing a file type. Valid values are the same as in [`filecon`](cil_file_labeling_statements.md#filecon) rules.</p></td> +</tr> +<tr class="odd"> <td align="left"><p><code>context_id</code></p></td> <td align="left"><p>A previously declared <code>context</code> identifier or an anonymous security context (<code>user role type levelrange</code>), the range MUST be defined whether the policy is MLS/MCS enabled or not.</p></td> </tr> |