• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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