Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # Copyright 2022 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 | import argparse |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 18 | import distutils.ccompiler |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 19 | import glob |
| 20 | import logging |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 21 | import mini_parser |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 22 | import os |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 23 | import policy |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 24 | import shutil |
| 25 | import subprocess |
| 26 | import tempfile |
| 27 | import zipfile |
| 28 | """This tool generates a mapping file for {ver} core sepolicy.""" |
| 29 | |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 30 | temp_dir = '' |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 31 | |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 32 | |
| 33 | def check_run(cmd, cwd=None): |
| 34 | if cwd: |
| 35 | logging.debug('Running cmd at %s: %s' % (cwd, cmd)) |
| 36 | else: |
| 37 | logging.debug('Running cmd: %s' % cmd) |
| 38 | subprocess.run(cmd, cwd=cwd, check=True) |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 39 | |
| 40 | |
| 41 | def check_output(cmd): |
| 42 | logging.debug('Running cmd: %s' % cmd) |
| 43 | return subprocess.run(cmd, check=True, stdout=subprocess.PIPE) |
| 44 | |
| 45 | |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 46 | def get_android_build_top(): |
| 47 | ANDROID_BUILD_TOP = os.getenv('ANDROID_BUILD_TOP') |
| 48 | if not ANDROID_BUILD_TOP: |
| 49 | sys.exit( |
| 50 | 'Error: Missing ANDROID_BUILD_TOP env variable. Please run ' |
| 51 | '\'. build/envsetup.sh; lunch <build target>\'. Exiting script.') |
| 52 | return ANDROID_BUILD_TOP |
| 53 | |
| 54 | |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 55 | def fetch_artifact(branch, build, pattern, destination='.'): |
| 56 | """Fetches build artifacts from Android Build server. |
| 57 | |
| 58 | Args: |
| 59 | branch: string, branch to pull build artifacts from |
| 60 | build: string, build ID or "latest" |
| 61 | pattern: string, pattern of build artifact file name |
| 62 | destination: string, destination to pull build artifact to |
| 63 | """ |
| 64 | fetch_artifact_path = '/google/data/ro/projects/android/fetch_artifact' |
| 65 | cmd = [ |
| 66 | fetch_artifact_path, '--branch', branch, '--target', |
| 67 | 'aosp_arm64-userdebug' |
| 68 | ] |
| 69 | if build == 'latest': |
| 70 | cmd.append('--latest') |
| 71 | else: |
| 72 | cmd.extend(['--bid', build]) |
| 73 | cmd.extend([pattern, destination]) |
| 74 | check_run(cmd) |
| 75 | |
| 76 | |
| 77 | def extract_mapping_file_from_img(img_path, ver, destination='.'): |
| 78 | """ Extracts system/etc/selinux/mapping/{ver}.cil from system.img file. |
| 79 | |
| 80 | Args: |
| 81 | img_path: string, path to system.img file |
| 82 | ver: string, version of designated mapping file |
| 83 | destination: string, destination to pull the mapping file to |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 84 | |
| 85 | Returns: |
| 86 | string, path to extracted mapping file |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 87 | """ |
| 88 | |
| 89 | cmd = [ |
| 90 | 'debugfs', '-R', |
| 91 | 'cat system/etc/selinux/mapping/%s.cil' % ver, img_path |
| 92 | ] |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 93 | path = os.path.join(destination, '%s.cil' % ver) |
| 94 | with open(path, 'wb') as f: |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 95 | logging.debug('Extracting %s.cil to %s' % (ver, destination)) |
| 96 | f.write(check_output(cmd).stdout) |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 97 | return path |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 98 | |
| 99 | |
| 100 | def download_mapping_file(branch, build, ver, destination='.'): |
| 101 | """ Downloads system/etc/selinux/mapping/{ver}.cil from Android Build server. |
| 102 | |
| 103 | Args: |
| 104 | branch: string, branch to pull build artifacts from (e.g. "sc-v2-dev") |
| 105 | build: string, build ID or "latest" |
| 106 | ver: string, version of designated mapping file (e.g. "32.0") |
| 107 | destination: string, destination to pull build artifact to |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 108 | |
| 109 | Returns: |
| 110 | string, path to extracted mapping file |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 111 | """ |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 112 | logging.info('Downloading %s mapping file from branch %s build %s...' % |
| 113 | (ver, branch, build)) |
| 114 | artifact_pattern = 'aosp_arm64-img-*.zip' |
| 115 | fetch_artifact(branch, build, artifact_pattern, temp_dir) |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 116 | |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 117 | # glob must succeed |
| 118 | zip_path = glob.glob(os.path.join(temp_dir, artifact_pattern))[0] |
| 119 | with zipfile.ZipFile(zip_path) as zip_file: |
| 120 | logging.debug('Extracting system.img to %s' % temp_dir) |
| 121 | zip_file.extract('system.img', temp_dir) |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 122 | |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 123 | system_img_path = os.path.join(temp_dir, 'system.img') |
| 124 | return extract_mapping_file_from_img(system_img_path, ver, destination) |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 125 | |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 126 | |
| 127 | def build_base_files(target_version): |
| 128 | """ Builds needed base policy files from the source code. |
| 129 | |
| 130 | Args: |
| 131 | target_version: string, target version to gerenate the mapping file |
| 132 | |
| 133 | Returns: |
| 134 | (string, string, string), paths to base policy, old policy, and pub policy |
| 135 | cil |
| 136 | """ |
| 137 | logging.info('building base sepolicy files') |
| 138 | build_top = get_android_build_top() |
| 139 | |
| 140 | cmd = [ |
| 141 | 'build/soong/soong_ui.bash', |
| 142 | '--make-mode', |
| 143 | 'dist', |
| 144 | 'base-sepolicy-files-for-mapping', |
| 145 | 'TARGET_PRODUCT=aosp_arm64', |
| 146 | 'TARGET_BUILD_VARIANT=userdebug', |
| 147 | ] |
| 148 | check_run(cmd, cwd=build_top) |
| 149 | |
| 150 | dist_dir = os.path.join(build_top, 'out', 'dist') |
| 151 | base_policy_path = os.path.join(dist_dir, 'base_plat_sepolicy') |
| 152 | old_policy_path = os.path.join(dist_dir, |
| 153 | '%s_plat_sepolicy' % target_version) |
| 154 | pub_policy_cil_path = os.path.join(dist_dir, 'base_plat_pub_policy.cil') |
| 155 | |
| 156 | return base_policy_path, old_policy_path, pub_policy_cil_path |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 157 | |
| 158 | |
| 159 | def get_args(): |
| 160 | parser = argparse.ArgumentParser() |
| 161 | parser.add_argument( |
| 162 | '--branch', |
| 163 | required=True, |
| 164 | help='Branch to pull build from. e.g. "sc-v2-dev"') |
| 165 | parser.add_argument('--build', required=True, help='Build ID, or "latest"') |
| 166 | parser.add_argument( |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 167 | '--target-version', |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 168 | required=True, |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 169 | help='Target version of designated mapping file. e.g. "32.0"') |
| 170 | parser.add_argument( |
| 171 | '--latest-version', |
| 172 | required=True, |
| 173 | help='Latest version for mapping of newer types. e.g. "31.0"') |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 174 | parser.add_argument( |
| 175 | '-v', |
| 176 | '--verbose', |
| 177 | action='count', |
| 178 | default=0, |
| 179 | help='Increase output verbosity, e.g. "-v", "-vv".') |
| 180 | return parser.parse_args() |
| 181 | |
| 182 | |
| 183 | def main(): |
| 184 | args = get_args() |
| 185 | |
| 186 | verbosity = min(args.verbose, 2) |
| 187 | logging.basicConfig( |
| 188 | format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s', |
| 189 | level=(logging.WARNING, logging.INFO, logging.DEBUG)[verbosity]) |
| 190 | |
Inseob Kim | cbc95ea | 2022-01-21 19:32:53 +0900 | [diff] [blame^] | 191 | global temp_dir |
| 192 | temp_dir = tempfile.mkdtemp() |
| 193 | |
| 194 | try: |
| 195 | libpath = os.path.join( |
| 196 | os.path.dirname(os.path.realpath(__file__)), 'libsepolwrap' + |
| 197 | distutils.ccompiler.new_compiler().shared_lib_extension) |
| 198 | if not os.path.exists(libpath): |
| 199 | sys.exit( |
| 200 | 'Error: libsepolwrap does not exist. Is this binary corrupted?\n' |
| 201 | ) |
| 202 | |
| 203 | build_top = get_android_build_top() |
| 204 | sepolicy_path = os.path.join(build_top, 'system', 'sepolicy') |
| 205 | target_compat_path = os.path.join(sepolicy_path, 'private', 'compat', |
| 206 | args.target_version) |
| 207 | |
| 208 | # Step 1. Download system/etc/selinux/mapping/{ver}.cil, and remove types/typeattributes |
| 209 | mapping_file = download_mapping_file(args.branch, args.build, |
| 210 | args.target_version) |
| 211 | mapping_file_cil = mini_parser.MiniCilParser(mapping_file) |
| 212 | mapping_file_cil.types = set() |
| 213 | mapping_file_cil.typeattributes = set() |
| 214 | |
| 215 | # Step 2. Build base policy files and parse latest mapping files |
| 216 | base_policy_path, old_policy_path, pub_policy_cil_path = build_base_files( |
| 217 | args.target_version) |
| 218 | base_policy = policy.Policy(base_policy_path, None, libpath) |
| 219 | old_policy = policy.Policy(old_policy_path, None, libpath) |
| 220 | pub_policy_cil = mini_parser.MiniCilParser(pub_policy_cil_path) |
| 221 | |
| 222 | all_types = base_policy.GetAllTypes(False) |
| 223 | old_all_types = old_policy.GetAllTypes(False) |
| 224 | pub_types = pub_policy_cil.types |
| 225 | |
| 226 | # Step 3. Find new types and removed types |
| 227 | new_types = pub_types & (all_types - old_all_types) |
| 228 | removed_types = (mapping_file_cil.pubtypes - mapping_file_cil.types) & ( |
| 229 | old_all_types - all_types) |
| 230 | |
| 231 | logging.info('new types: %s' % new_types) |
| 232 | logging.info('removed types: %s' % removed_types) |
| 233 | |
| 234 | # TODO: Step 4. Map new types and removed types appropriately |
| 235 | finally: |
| 236 | logging.info('Deleting temporary dir: {}'.format(temp_dir)) |
| 237 | shutil.rmtree(temp_dir) |
Inseob Kim | 29e357e | 2022-01-17 16:37:51 +0900 | [diff] [blame] | 238 | |
| 239 | |
| 240 | if __name__ == '__main__': |
| 241 | main() |