blob: ce0548a44e8f2f8d3e74a38068998ae2b2c5623c [file] [log] [blame]
ThiƩbaud Weksteenb05a1a12021-12-06 15:23:18 +11001#!/usr/bin/env python3
2#
Bowgo Tsai741a70a2018-02-05 17:41:02 +08003# 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()
Tri Vod57789f2018-12-17 16:35:41 -080032_SUPPORTED_COMMANDS = ('build_cil', 'filter_out')
Bowgo Tsai741a70a2018-02-05 17:41:02 +080033
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
Tri Vod57789f2018-12-17 16:35:41 -0800124def 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
Bowgo Tsai741a70a2018-02-05 17:41:02 +0800142def 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)