build_sepolicy.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. """Command-line tool to build SEPolicy files."""
  15. import argparse
  16. import os
  17. import subprocess
  18. import sys
  19. import file_utils
  20. # All supported commands in this module.
  21. # For each command, need to add two functions. Take 'build_cil' for example:
  22. # - setup_build_cil()
  23. # - Sets up command parsers and sets default function to do_build_cil().
  24. # - do_build_cil()
  25. _SUPPORTED_COMMANDS = ('build_cil', 'filter_out')
  26. def run_host_command(args, **kwargs):
  27. """Runs a host command and prints output."""
  28. if kwargs.get('shell'):
  29. command_log = args
  30. else:
  31. command_log = ' '.join(args) # For args as a sequence.
  32. try:
  33. subprocess.check_call(args, **kwargs)
  34. except subprocess.CalledProcessError as err:
  35. sys.stderr.write(
  36. 'build_sepolicy - failed to run command: {!r} (ret:{})\n'.format(
  37. command_log, err.returncode))
  38. sys.exit(err.returncode)
  39. def do_build_cil(args):
  40. """Builds a sepolicy CIL (Common Intermediate Language) file.
  41. This functions invokes some host utils (e.g., secilc, checkpolicy,
  42. version_sepolicy) to generate a .cil file.
  43. Args:
  44. args: the parsed command arguments.
  45. """
  46. # Determines the raw CIL file name.
  47. input_file_name = os.path.splitext(args.input_policy_conf)[0]
  48. raw_cil_file = input_file_name + '_raw.cil'
  49. # Builds the raw CIL.
  50. file_utils.make_parent_dirs(raw_cil_file)
  51. checkpolicy_cmd = [args.checkpolicy_env]
  52. checkpolicy_cmd += [os.path.join(args.android_host_path, 'checkpolicy'),
  53. '-C', '-M', '-c', args.policy_vers,
  54. '-o', raw_cil_file, args.input_policy_conf]
  55. # Using shell=True to setup args.checkpolicy_env variables.
  56. run_host_command(' '.join(checkpolicy_cmd), shell=True)
  57. file_utils.filter_out([args.reqd_mask], raw_cil_file)
  58. # Builds the output CIL by versioning the above raw CIL.
  59. output_file = args.output_cil
  60. if output_file is None:
  61. output_file = input_file_name + '.cil'
  62. file_utils.make_parent_dirs(output_file)
  63. run_host_command([os.path.join(args.android_host_path, 'version_policy'),
  64. '-b', args.base_policy, '-t', raw_cil_file,
  65. '-n', args.treble_sepolicy_vers, '-o', output_file])
  66. if args.filter_out_files:
  67. file_utils.filter_out(args.filter_out_files, output_file)
  68. # Tests that the output file can be merged with the given CILs.
  69. if args.dependent_cils:
  70. merge_cmd = [os.path.join(args.android_host_path, 'secilc'),
  71. '-m', '-M', 'true', '-G', '-N', '-c', args.policy_vers]
  72. merge_cmd += args.dependent_cils # the give CILs to merge
  73. merge_cmd += [output_file, '-o', '/dev/null', '-f', '/dev/null']
  74. run_host_command(merge_cmd)
  75. def setup_build_cil(subparsers):
  76. """Sets up command args for 'build_cil' command."""
  77. # Required arguments.
  78. parser = subparsers.add_parser('build_cil', help='build CIL files')
  79. parser.add_argument('-i', '--input_policy_conf', required=True,
  80. help='source policy.conf')
  81. parser.add_argument('-m', '--reqd_mask', required=True,
  82. help='the bare minimum policy.conf to use checkpolicy')
  83. parser.add_argument('-b', '--base_policy', required=True,
  84. help='base policy for versioning')
  85. parser.add_argument('-t', '--treble_sepolicy_vers', required=True,
  86. help='the version number to use for Treble-OTA')
  87. parser.add_argument('-p', '--policy_vers', required=True,
  88. help='SELinux policy version')
  89. # Optional arguments.
  90. parser.add_argument('-c', '--checkpolicy_env',
  91. help='environment variables passed to checkpolicy')
  92. parser.add_argument('-f', '--filter_out_files', nargs='+',
  93. help='the pattern files to filter out the output cil')
  94. parser.add_argument('-d', '--dependent_cils', nargs='+',
  95. help=('check the output file can be merged with '
  96. 'the dependent cil files'))
  97. parser.add_argument('-o', '--output_cil', help='the output cil file')
  98. # The function that performs the actual works.
  99. parser.set_defaults(func=do_build_cil)
  100. def do_filter_out(args):
  101. """Removes all lines in one file that match any line in another file.
  102. Args:
  103. args: the parsed command arguments.
  104. """
  105. file_utils.filter_out(args.filter_out_files, args.target_file)
  106. def setup_filter_out(subparsers):
  107. """Sets up command args for 'filter_out' command."""
  108. parser = subparsers.add_parser('filter_out', help='filter CIL files')
  109. parser.add_argument('-f', '--filter_out_files', required=True, nargs='+',
  110. help='the pattern files to filter out the output cil')
  111. parser.add_argument('-t', '--target_file', required=True,
  112. help='target file to filter')
  113. parser.set_defaults(func=do_filter_out)
  114. def run(argv):
  115. """Sets up command parser and execuates sub-command."""
  116. parser = argparse.ArgumentParser()
  117. # Adds top-level arguments.
  118. parser.add_argument('-a', '--android_host_path', default='',
  119. help='a path to host out executables')
  120. # Adds subparsers for each COMMAND.
  121. subparsers = parser.add_subparsers(title='COMMAND')
  122. for command in _SUPPORTED_COMMANDS:
  123. globals()['setup_' + command](subparsers)
  124. args = parser.parse_args(argv[1:])
  125. args.func(args)
  126. if __name__ == '__main__':
  127. run(sys.argv)