diff options
author | Konrad Lipinski <k.lipinski2@partner.samsung.com> | 2018-09-14 14:14:17 +0200 |
---|---|---|
committer | Konrad Lipinski <k.lipinski2@partner.samsung.com> | 2018-10-10 15:53:59 +0200 |
commit | 75293c9c70f3f04c86721039bedfd6e0bf0786a8 (patch) | |
tree | 0df3266bd2eb1ee79895deeab61ccb06ecc03c61 /policy | |
parent | 4e5b938c96cf8a76606417c4ae2bfadb0b6e7fbe (diff) | |
download | security-manager-75293c9c70f3f04c86721039bedfd6e0bf0786a8.tar.gz security-manager-75293c9c70f3f04c86721039bedfd6e0bf0786a8.tar.bz2 security-manager-75293c9c70f3f04c86721039bedfd6e0bf0786a8.zip |
Replace smack rule storage with straight-from-db rule loader
Details:
* remove %{TZ_SYS_VAR}/security-manager/rules{,-merged} directories
* add security-manager-rules-loader that
** performs database migration/recovery
** writes smack rules from a coherent database directly to load2
* add generate-rule-code generator that translates rule templates
(*.smack files) into c++ code for use in the loader
* remove security-manager-init-db binary and replace its invocation with
sh$ security-manager-rules-loader no-load
* replace dd invocation with security-manager-rules-loader in the rule
loader service
* add explicit dependency to ensure the loader runs before the manager
* refactor manager code
** remove the majority of database migration/recovery code on grounds of
loader having run beforehand
** replace defensive remnants of said code with an emergency invocation
sh$ security-manager-rules-loader fallback-only
to apply fallback on database schmea errors
** remove rule file maintenance (not needed anymore)
TODO:
* *.smack template files are still used by the manager at runtime,
removing them is optional and would require a substantial refactor
best placed in a separate commit
Pros:
* optimize flash usage (rule files were prone to quadratic explosion)
* solve database-rulefiles coherence problem
* make the rule loader performance more scalable and typically better
* simplify and speed up the manager a bit by dropping rule file code
Change-Id: I7d79d5ec7e66c9dfe6563dbb3f76bf6ab6669589
Diffstat (limited to 'policy')
-rw-r--r-- | policy/CMakeLists.txt | 10 | ||||
-rwxr-xr-x | policy/generate-rule-code | 152 | ||||
-rwxr-xr-x | policy/updates/update-policy-to-v7.sh | 11 |
3 files changed, 173 insertions, 0 deletions
diff --git a/policy/CMakeLists.txt b/policy/CMakeLists.txt index a4ed18d6..381245d7 100644 --- a/policy/CMakeLists.txt +++ b/policy/CMakeLists.txt @@ -17,3 +17,13 @@ INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/security-manager-policy-reload DEST # FOTA updater INSTALL(FILES 241.security-manager.policy-update.sh DESTINATION ${FOTA_DIR}) + +SET(GEN_FILE ${GEN_PATH}/generated-rule-templates.h) +SET(GENERATOR "./generate-rule-code") + +ADD_CUSTOM_COMMAND(OUTPUT ${GEN_FILE} + COMMAND mkdir -p "${GEN_PATH}" + COMMAND ${GENERATOR} *.smack > ${GEN_FILE} + DEPENDS ${GENERATOR} "*.smack" +) +ADD_CUSTOM_TARGET(generate_rule_template_code DEPENDS ${GEN_FILE}) diff --git a/policy/generate-rule-code b/policy/generate-rule-code new file mode 100755 index 00000000..50c270e8 --- /dev/null +++ b/policy/generate-rule-code @@ -0,0 +1,152 @@ +#!/usr/bin/env perl +# Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# the generated file is included verbatim into security-manager-rules-writer.cpp +# therein lie more comments + +use strict; use warnings; use 5.10.1; +sub member { my $m = shift; grep { $_ eq $m } @_ } + +# rule groups (roughly correspond to variables used and functions generated) +my @rulesAuthor; +my @rulesPkgLabelAuthor; +my @rulesPkgLabel; +my @rulesPathRW; +my @rulesPath; +my @rulesSharedRO; + +# one rule per line, either on stdin or in file arguments +my @lines; +while (<>) { chomp; push @lines, $_; } + +# process lines in sorted order so that similar rules are clustered together +for (sort @lines) { + # make sure the rule is canonical - it's important for the writer (which makes assumptions about rule width and such) + die "heading whitespace ($_)" if /^\s/; + die "trailing whitespace ($_)" if /\s$/; + die "non-space whitespace ($_)" if grep {/[^ ]/} /(\s)/g; + die "two or more consecutive spaces ($_)" if / /; + + # the rule is split into an odd number of segments (3 or 5): verbatim variable verbatim (variable verbatim)? + # ex. "System ~PROCESS~ rwxat" -> ("System ", "PROCESS", " rwxat") + # even elements are variables, odd ones verbatim strings + my @segments = split /~/; + + # segment-based validation + die "rule without variables ($_)" if @segments < 2; + die "too many variables ($_)" if @segments > 5; + die "even number of segments ($_)" if !(@segments % 2); + die "ending segment empty ($_)" if !length $segments[-1]; + my %varCount; + for (@segments[grep {$_%2} 0..$#segments]) { + die "unknown var ($_)" if !member $_, qw(PATH_TRUSTED PROCESS PATH_RO PATH_RW PATH_SHARED_RO); + ++$varCount{$_}; + } + die "first segment ending with non-space character ($_)" if length $segments[0] && $segments[0] !~ / $/; + die "last segment starting with non-space character ($_)" if length $segments[-1] && $segments[-1] !~ /^ /; + die "variables not surrounded with whitespace ($_)" if grep {/^$/ || /^[^ ]/ || /[^ ]$/} @segments[grep {!($_%2)} 2..$#segments-2]; + + # make sure the permission string is a valid constant + my ($prePerm, $permChars) = $segments[-1] =~ /(.* )([^ ]*)$/; + my %perm; + for (split //, $permChars) { + die "unknown permission char ($_) in ($segments[-1])" if !member $_, split //, "rwxatlb"; + die "duplicate permission char ($_) in ($segments[-1])" if exists $perm{$_}; + $perm{$_} = 1; + } + + # sort permission string characters to improve constant sharing in the writer + $segments[-1] = $prePerm . join '', grep {exists $perm{$_}} split //, "rwxatlb"; + + # partition rules into rough groups + if (1 == keys %varCount) { + # single variable rules + if (exists $varCount{PATH_TRUSTED}) { + push @rulesAuthor, [@segments]; + } elsif (exists $varCount{PATH_SHARED_RO}) { + push @rulesSharedRO, [@segments]; + } elsif (exists $varCount{PATH_RO}) { + push @rulesPath, [@segments]; + } elsif (exists $varCount{PATH_RW}) { + push @rulesPathRW, [@segments]; + } else { # PROCESS + push @rulesPkgLabel, [@segments]; + } + } else { + # multi variable rules + die "multi-variable rule ($_) does not contain ~PROCESS~" if !exists $varCount{PROCESS}; + if (exists $varCount{PATH_TRUSTED}) { + push @rulesPkgLabelAuthor, [@segments]; + } elsif (exists $varCount{PATH_RO} || exists $varCount{PATH_RW}) { + push @rulesPkgLabel, [@segments]; + } else { + die "unsupported multi-variable rule ($_)"; + } + } +} + +# for non-hybrid packages, ~PATH_RW~ == ~PROCESS~ +# this may lead to rule duplication between @rulesPathRW and @rulesPkgLabel +# +# in order to avoid this, @rulesPathRW is split into two groups: +# rules having an isomorphic ~PROCESS~ rule end up in @rulesPathRWHybridOnly (not to be applied to non-hybrid packages) +# other rules end up in @rulesPath (applied to all packages) +my @pureProcessRulesAsPathRWRule = map {3 != @$_ ? () : ($_->[0].'~PATH_RW~'.$_->[2])} @rulesPkgLabel; +my @rulesPathRWHybridOnly; +push @rulesPath, grep { + my $asRule = $_->[0].'~PATH_RW~'.$_->[2]; + my $hasRedudnantProcessRule = member $asRule, @pureProcessRulesAsPathRWRule; + push @rulesPathRWHybridOnly, $_ if $hasRedudnantProcessRule; + !$hasRedudnantProcessRule; +} @rulesPathRW; + +# generate a function that writes a sequence of rules +sub rules { + my $functionName = shift; + my $mustBeNonempty = shift; + die "($functionName()) has no rules - remove the call and propagate to make sure the loader stays fast" if $mustBeNonempty && !@_; + say "inl void $functionName() {"; + for (@_) { + my @segments = @$_; + print " rule("; + for (0..$#segments/2-1) { + my $verbatim = $segments[2*$_]; + my $wildcard = $segments[2*$_ + 1]; + my $var = 'pkgL'; + if ($wildcard eq 'PROCESS') { + $segments[2*$_+2] =~ s/^ //; + $var = 'pl'; + } elsif ($wildcard eq 'PATH_TRUSTED') { + $verbatim .= 'User::Author::'; + $var = 'pathTrusted'; + } elsif ($wildcard eq 'PATH_RO') { + $segments[2*$_+2] = '::RO'.$segments[2*$_+2]; + } elsif ($wildcard eq 'PATH_SHARED_RO') { + $segments[2*$_+2] = '::SharedRO'.$segments[2*$_+2]; + } + print "\"$verbatim\", " if length $verbatim; + print "$var, "; + } + say "\"$segments[-1]\");"; + } + say "}"; +} + +rules 'rulesAuthor', 0, @rulesAuthor; +rules 'rulesPkgLabelAuthor', 1, @rulesPkgLabelAuthor; +rules 'rulesPkgLabel', 0, @rulesPkgLabel; +rules 'rulesPathRWHybridOnly', 0, @rulesPathRWHybridOnly; +rules 'rulesPath', 0, @rulesPath; +rules 'rulesSharedRO', 0, @rulesSharedRO; diff --git a/policy/updates/update-policy-to-v7.sh b/policy/updates/update-policy-to-v7.sh new file mode 100755 index 00000000..4f0b4508 --- /dev/null +++ b/policy/updates/update-policy-to-v7.sh @@ -0,0 +1,11 @@ +#!/bin/sh -e + +export PATH=/sbin:/usr/sbin:/bin:/usr/bin + +. /etc/tizen-platform.conf + +systemctl stop security-manager.service security-manager.socket + +rm -rf "$TZ_SYS_VAR"/security-manager/rules{,-merged} + +systemctl start security-manager.service security-manager.socket |