Using a python script to build sepolicy
Current sepolicy CIL files are built by several command-line tools in Android.mk. This change extracts some of the build logic into a python script to relief the effort in Android.mk. The first command is `build_sepolicy build_cil`. It's possible to add more sub-commands under the build_sepolicy script in the future. Bug: 64240127 Test: build bullhead/taimen Change-Id: Ie0ae4fc5256a550c72954cde5d5dd213a22d159a
This commit is contained in:
parent
afbcf21bfb
commit
741a70a058
5 changed files with 244 additions and 27 deletions
|
@ -1 +1,4 @@
|
|||
subdirs = ["tests"]
|
||||
subdirs = [
|
||||
"tests",
|
||||
"build",
|
||||
]
|
||||
|
|
43
Android.mk
43
Android.mk
|
@ -228,11 +228,14 @@ ifneq ($(PRODUCT_PRECOMPILED_SEPOLICY),false)
|
|||
LOCAL_REQUIRED_MODULES += precompiled_sepolicy precompiled_sepolicy.plat_and_mapping.sha256
|
||||
endif
|
||||
else
|
||||
# Use monolithic SELinux policy
|
||||
LOCAL_REQUIRED_MODULES += sepolicy
|
||||
# The following files are only allowed for non-Treble devices.
|
||||
LOCAL_REQUIRED_MODULES += \
|
||||
sepolicy \
|
||||
vendor_service_contexts
|
||||
endif
|
||||
|
||||
LOCAL_REQUIRED_MODULES += \
|
||||
build_sepolicy \
|
||||
vendor_file_contexts \
|
||||
vendor_mac_permissions.xml \
|
||||
vendor_property_contexts \
|
||||
|
@ -247,10 +250,6 @@ LOCAL_REQUIRED_MODULES += \
|
|||
searchpolicy \
|
||||
vndservice_contexts \
|
||||
|
||||
ifneq ($(PRODUCT_SEPOLICY_SPLIT),true)
|
||||
LOCAL_REQUIRED_MODULES += vendor_service_contexts
|
||||
endif
|
||||
|
||||
ifneq ($(TARGET_BUILD_VARIANT), user)
|
||||
LOCAL_REQUIRED_MODULES += \
|
||||
selinux_denial_metadata \
|
||||
|
@ -540,31 +539,23 @@ $(PLAT_PUBLIC_POLICY) $(REQD_MASK_POLICY) $(PLAT_VENDOR_POLICY) $(BOARD_VENDOR_S
|
|||
$(transform-policy-to-conf)
|
||||
$(hide) sed '/dontaudit/d' $@ > $@.dontaudit
|
||||
|
||||
vendor_policy_raw := $(intermediates)/vendor_policy_raw.cil
|
||||
$(vendor_policy_raw): PRIVATE_POL_CONF := $(vendor_policy.conf)
|
||||
$(vendor_policy_raw): PRIVATE_REQD_MASK := $(reqd_policy_mask.cil)
|
||||
$(vendor_policy_raw): $(HOST_OUT_EXECUTABLES)/checkpolicy $(vendor_policy.conf) \
|
||||
$(reqd_policy_mask.cil)
|
||||
@mkdir -p $(dir $@)
|
||||
$(hide) $(CHECKPOLICY_ASAN_OPTIONS) $< -C -M -c $(POLICYVERS) -o $@.tmp $(PRIVATE_POL_CONF)
|
||||
$(hide) grep -Fxv -f $(PRIVATE_REQD_MASK) $@.tmp > $@
|
||||
|
||||
$(LOCAL_BUILT_MODULE) : PRIVATE_VERS := $(BOARD_SEPOLICY_VERS)
|
||||
$(LOCAL_BUILT_MODULE) : PRIVATE_TGT_POL := $(vendor_policy_raw)
|
||||
$(LOCAL_BUILT_MODULE) : PRIVATE_DEP_CIL_FILES := $(built_plat_cil) $(built_plat_pub_vers_cil) $(built_mapping_cil)
|
||||
$(LOCAL_BUILT_MODULE) : PRIVATE_FILTER_CIL := $(built_plat_pub_vers_cil)
|
||||
$(LOCAL_BUILT_MODULE) : $(plat_pub_policy.cil) $(vendor_policy_raw) \
|
||||
$(HOST_OUT_EXECUTABLES)/version_policy $(HOST_OUT_EXECUTABLES)/secilc \
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_POL_CONF := $(vendor_policy.conf)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_REQD_MASK := $(reqd_policy_mask.cil)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_BASE_CIL := $(plat_pub_policy.cil)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_VERS := $(BOARD_SEPOLICY_VERS)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_DEP_CIL_FILES := $(built_plat_cil) $(built_plat_pub_vers_cil) $(built_mapping_cil)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_FILTER_CIL := $(built_plat_pub_vers_cil)
|
||||
$(LOCAL_BUILT_MODULE): $(HOST_OUT_EXECUTABLES)/build_sepolicy \
|
||||
$(vendor_policy.conf) $(reqd_policy_mask.cil) $(plat_pub_policy.cil) \
|
||||
$(built_plat_cil) $(built_plat_pub_vers_cil) $(built_mapping_cil)
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_OUT_EXECUTABLES)/version_policy -b $< -t $(PRIVATE_TGT_POL) -n $(PRIVATE_VERS) -o $@.tmp
|
||||
$(hide) grep -Fxv -f $(PRIVATE_FILTER_CIL) $@.tmp > $@
|
||||
$(hide) $(HOST_OUT_EXECUTABLES)/secilc -m -M true -G -N -c $(POLICYVERS) \
|
||||
$(PRIVATE_DEP_CIL_FILES) $@ -o /dev/null -f /dev/null
|
||||
$(hide) $(HOST_OUT_EXECUTABLES)/build_sepolicy -a $(HOST_OUT_EXECUTABLES) build_cil \
|
||||
-i $(PRIVATE_POL_CONF) -m $(PRIVATE_REQD_MASK) -c $(CHECKPOLICY_ASAN_OPTIONS) \
|
||||
-b $(PRIVATE_BASE_CIL) -d $(PRIVATE_DEP_CIL_FILES) -f $(PRIVATE_FILTER_CIL) \
|
||||
-t $(PRIVATE_VERS) -p $(POLICYVERS) -o $@
|
||||
|
||||
built_vendor_cil := $(LOCAL_BUILT_MODULE)
|
||||
vendor_policy.conf :=
|
||||
vendor_policy_raw :=
|
||||
|
||||
#################################
|
||||
include $(CLEAR_VARS)
|
||||
|
|
34
build/Android.bp
Normal file
34
build/Android.bp
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright (C) 2018 The Android Open Source Project
|
||||
//
|
||||
// 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.
|
||||
|
||||
python_binary_host {
|
||||
name: "build_sepolicy",
|
||||
srcs: [
|
||||
"build_sepolicy.py",
|
||||
"file_utils.py",
|
||||
],
|
||||
required: [
|
||||
"checkpolicy",
|
||||
"secilc",
|
||||
"version_policy",
|
||||
],
|
||||
version: {
|
||||
py2: {
|
||||
enabled: true,
|
||||
},
|
||||
py3: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
140
build/build_sepolicy.py
Normal file
140
build/build_sepolicy.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
# Copyright 2018 - The Android Open Source Project
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Command-line tool to build SEPolicy files."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import file_utils
|
||||
|
||||
|
||||
# All supported commands in this module.
|
||||
# For each command, need to add two functions. Take 'build_cil' for example:
|
||||
# - setup_build_cil()
|
||||
# - Sets up command parsers and sets default function to do_build_cil().
|
||||
# - do_build_cil()
|
||||
_SUPPORTED_COMMANDS = ('build_cil',)
|
||||
|
||||
|
||||
def run_host_command(args, **kwargs):
|
||||
"""Runs a host command and prints output."""
|
||||
if kwargs.get('shell'):
|
||||
command_log = args
|
||||
else:
|
||||
command_log = ' '.join(args) # For args as a sequence.
|
||||
|
||||
try:
|
||||
subprocess.check_call(args, **kwargs)
|
||||
except subprocess.CalledProcessError as err:
|
||||
sys.stderr.write(
|
||||
'build_sepolicy - failed to run command: {!r} (ret:{})\n'.format(
|
||||
command_log, err.returncode))
|
||||
sys.exit(err.returncode)
|
||||
|
||||
|
||||
def do_build_cil(args):
|
||||
"""Builds a sepolicy CIL (Common Intermediate Language) file.
|
||||
|
||||
This functions invokes some host utils (e.g., secilc, checkpolicy,
|
||||
version_sepolicy) to generate a .cil file.
|
||||
|
||||
Args:
|
||||
args: the parsed command arguments.
|
||||
"""
|
||||
# Determines the raw CIL file name.
|
||||
input_file_name = os.path.splitext(args.input_policy_conf)[0]
|
||||
raw_cil_file = input_file_name + '_raw.cil'
|
||||
# Builds the raw CIL.
|
||||
file_utils.make_parent_dirs(raw_cil_file)
|
||||
checkpolicy_cmd = [args.checkpolicy_env]
|
||||
checkpolicy_cmd += [os.path.join(args.android_host_path, 'checkpolicy'),
|
||||
'-C', '-M', '-c', args.policy_vers,
|
||||
'-o', raw_cil_file, args.input_policy_conf]
|
||||
# Using shell=True to setup args.checkpolicy_env variables.
|
||||
run_host_command(' '.join(checkpolicy_cmd), shell=True)
|
||||
file_utils.filter_out([args.reqd_mask], raw_cil_file)
|
||||
|
||||
# Builds the output CIL by versioning the above raw CIL.
|
||||
output_file = args.output_cil
|
||||
if output_file is None:
|
||||
output_file = input_file_name + '.cil'
|
||||
file_utils.make_parent_dirs(output_file)
|
||||
|
||||
run_host_command([os.path.join(args.android_host_path, 'version_policy'),
|
||||
'-b', args.base_policy, '-t', raw_cil_file,
|
||||
'-n', args.treble_sepolicy_vers, '-o', output_file])
|
||||
if args.filter_out_files:
|
||||
file_utils.filter_out(args.filter_out_files, output_file)
|
||||
|
||||
# Tests that the output file can be merged with the given CILs.
|
||||
if args.dependent_cils:
|
||||
merge_cmd = [os.path.join(args.android_host_path, 'secilc'),
|
||||
'-m', '-M', 'true', '-G', '-N', '-c', args.policy_vers]
|
||||
merge_cmd += args.dependent_cils # the give CILs to merge
|
||||
merge_cmd += [output_file, '-o', '/dev/null', '-f', '/dev/null']
|
||||
run_host_command(merge_cmd)
|
||||
|
||||
|
||||
def setup_build_cil(subparsers):
|
||||
"""Sets up command args for 'build_cil' command."""
|
||||
|
||||
# Required arguments.
|
||||
parser = subparsers.add_parser('build_cil', help='build CIL files')
|
||||
parser.add_argument('-i', '--input_policy_conf', required=True,
|
||||
help='source policy.conf')
|
||||
parser.add_argument('-m', '--reqd_mask', required=True,
|
||||
help='the bare minimum policy.conf to use checkpolicy')
|
||||
parser.add_argument('-b', '--base_policy', required=True,
|
||||
help='base policy for versioning')
|
||||
parser.add_argument('-t', '--treble_sepolicy_vers', required=True,
|
||||
help='the version number to use for Treble-OTA')
|
||||
parser.add_argument('-p', '--policy_vers', required=True,
|
||||
help='SELinux policy version')
|
||||
|
||||
# Optional arguments.
|
||||
parser.add_argument('-c', '--checkpolicy_env',
|
||||
help='environment variables passed to checkpolicy')
|
||||
parser.add_argument('-f', '--filter_out_files', nargs='+',
|
||||
help='the pattern files to filter out the output cil')
|
||||
parser.add_argument('-d', '--dependent_cils', nargs='+',
|
||||
help=('check the output file can be merged with '
|
||||
'the dependent cil files'))
|
||||
parser.add_argument('-o', '--output_cil', help='the output cil file')
|
||||
|
||||
# The function that performs the actual works.
|
||||
parser.set_defaults(func=do_build_cil)
|
||||
|
||||
|
||||
def run(argv):
|
||||
"""Sets up command parser and execuates sub-command."""
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
# Adds top-level arguments.
|
||||
parser.add_argument('-a', '--android_host_path', default='',
|
||||
help='a path to host out executables')
|
||||
|
||||
# Adds subparsers for each COMMAND.
|
||||
subparsers = parser.add_subparsers(title='COMMAND')
|
||||
for command in _SUPPORTED_COMMANDS:
|
||||
globals()['setup_' + command](subparsers)
|
||||
|
||||
args = parser.parse_args(argv[1:])
|
||||
args.func(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run(sys.argv)
|
49
build/file_utils.py
Normal file
49
build/file_utils.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Copyright 2018 - The Android Open Source Project
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""File-related utilities."""
|
||||
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
|
||||
def make_parent_dirs(file_path):
|
||||
"""Creates parent directories for the file_path."""
|
||||
if os.path.exists(file_path):
|
||||
return
|
||||
|
||||
parent_dir = os.path.dirname(file_path)
|
||||
if parent_dir and not os.path.exists(parent_dir):
|
||||
os.makedirs(parent_dir)
|
||||
|
||||
|
||||
def filter_out(pattern_files, input_file):
|
||||
""""Removes lines in input_file that match any line in pattern_files."""
|
||||
|
||||
# Prepares patterns.
|
||||
patterns = []
|
||||
for f in pattern_files:
|
||||
patterns.extend(open(f).readlines())
|
||||
|
||||
# Copy lines that are not in the pattern.
|
||||
tmp_output = tempfile.NamedTemporaryFile()
|
||||
with open(input_file, 'r') as in_file:
|
||||
tmp_output.writelines(line for line in in_file.readlines()
|
||||
if line not in patterns)
|
||||
tmp_output.flush()
|
||||
|
||||
# Replaces the input_file.
|
||||
shutil.copyfile(tmp_output.name, input_file)
|
Loading…
Reference in a new issue