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