1#!/usr/bin/env python3 2# 3# Copyright 2018 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Command-line tool to build SEPolicy files.""" 18 19import argparse 20import os 21import subprocess 22import sys 23 24import file_utils 25 26 27# All supported commands in this module. 28# For each command, need to add two functions. Take 'build_cil' for example: 29# - setup_build_cil() 30# - Sets up command parsers and sets default function to do_build_cil(). 31# - do_build_cil() 32_SUPPORTED_COMMANDS = ('build_cil', 'filter_out') 33 34 35def run_host_command(args, **kwargs): 36 """Runs a host command and prints output.""" 37 if kwargs.get('shell'): 38 command_log = args 39 else: 40 command_log = ' '.join(args) # For args as a sequence. 41 42 try: 43 subprocess.check_call(args, **kwargs) 44 except subprocess.CalledProcessError as err: 45 sys.stderr.write( 46 'build_sepolicy - failed to run command: {!r} (ret:{})\n'.format( 47 command_log, err.returncode)) 48 sys.exit(err.returncode) 49 50 51def do_build_cil(args): 52 """Builds a sepolicy CIL (Common Intermediate Language) file. 53 54 This functions invokes some host utils (e.g., secilc, checkpolicy, 55 version_sepolicy) to generate a .cil file. 56 57 Args: 58 args: the parsed command arguments. 59 """ 60 # Determines the raw CIL file name. 61 input_file_name = os.path.splitext(args.input_policy_conf)[0] 62 raw_cil_file = input_file_name + '_raw.cil' 63 # Builds the raw CIL. 64 file_utils.make_parent_dirs(raw_cil_file) 65 checkpolicy_cmd = [args.checkpolicy_env] 66 checkpolicy_cmd += [os.path.join(args.android_host_path, 'checkpolicy'), 67 '-C', '-M', '-c', args.policy_vers, 68 '-o', raw_cil_file, args.input_policy_conf] 69 # Using shell=True to setup args.checkpolicy_env variables. 70 run_host_command(' '.join(checkpolicy_cmd), shell=True) 71 file_utils.filter_out([args.reqd_mask], raw_cil_file) 72 73 # Builds the output CIL by versioning the above raw CIL. 74 output_file = args.output_cil 75 if output_file is None: 76 output_file = input_file_name + '.cil' 77 file_utils.make_parent_dirs(output_file) 78 79 run_host_command([os.path.join(args.android_host_path, 'version_policy'), 80 '-b', args.base_policy, '-t', raw_cil_file, 81 '-n', args.treble_sepolicy_vers, '-o', output_file]) 82 if args.filter_out_files: 83 file_utils.filter_out(args.filter_out_files, output_file) 84 85 # Tests that the output file can be merged with the given CILs. 86 if args.dependent_cils: 87 merge_cmd = [os.path.join(args.android_host_path, 'secilc'), 88 '-m', '-M', 'true', '-G', '-N', '-c', args.policy_vers] 89 merge_cmd += args.dependent_cils # the give CILs to merge 90 merge_cmd += [output_file, '-o', '/dev/null', '-f', '/dev/null'] 91 run_host_command(merge_cmd) 92 93 94def setup_build_cil(subparsers): 95 """Sets up command args for 'build_cil' command.""" 96 97 # Required arguments. 98 parser = subparsers.add_parser('build_cil', help='build CIL files') 99 parser.add_argument('-i', '--input_policy_conf', required=True, 100 help='source policy.conf') 101 parser.add_argument('-m', '--reqd_mask', required=True, 102 help='the bare minimum policy.conf to use checkpolicy') 103 parser.add_argument('-b', '--base_policy', required=True, 104 help='base policy for versioning') 105 parser.add_argument('-t', '--treble_sepolicy_vers', required=True, 106 help='the version number to use for Treble-OTA') 107 parser.add_argument('-p', '--policy_vers', required=True, 108 help='SELinux policy version') 109 110 # Optional arguments. 111 parser.add_argument('-c', '--checkpolicy_env', 112 help='environment variables passed to checkpolicy') 113 parser.add_argument('-f', '--filter_out_files', nargs='+', 114 help='the pattern files to filter out the output cil') 115 parser.add_argument('-d', '--dependent_cils', nargs='+', 116 help=('check the output file can be merged with ' 117 'the dependent cil files')) 118 parser.add_argument('-o', '--output_cil', help='the output cil file') 119 120 # The function that performs the actual works. 121 parser.set_defaults(func=do_build_cil) 122 123 124def do_filter_out(args): 125 """Removes all lines in one file that match any line in another file. 126 127 Args: 128 args: the parsed command arguments. 129 """ 130 file_utils.filter_out(args.filter_out_files, args.target_file) 131 132def setup_filter_out(subparsers): 133 """Sets up command args for 'filter_out' command.""" 134 parser = subparsers.add_parser('filter_out', help='filter CIL files') 135 parser.add_argument('-f', '--filter_out_files', required=True, nargs='+', 136 help='the pattern files to filter out the output cil') 137 parser.add_argument('-t', '--target_file', required=True, 138 help='target file to filter') 139 parser.set_defaults(func=do_filter_out) 140 141 142def run(argv): 143 """Sets up command parser and execuates sub-command.""" 144 parser = argparse.ArgumentParser() 145 146 # Adds top-level arguments. 147 parser.add_argument('-a', '--android_host_path', default='', 148 help='a path to host out executables') 149 150 # Adds subparsers for each COMMAND. 151 subparsers = parser.add_subparsers(title='COMMAND') 152 for command in _SUPPORTED_COMMANDS: 153 globals()['setup_' + command](subparsers) 154 155 args = parser.parse_args(argv[1:]) 156 args.func(args) 157 158 159if __name__ == '__main__': 160 run(sys.argv) 161