1*105f6285SAndroid Build Coastguard Worker#!/usr/bin/python3 2*105f6285SAndroid Build Coastguard Worker# 3*105f6285SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project 4*105f6285SAndroid Build Coastguard Worker# 5*105f6285SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); you may not 6*105f6285SAndroid Build Coastguard Worker# use this file except in compliance with the License. You may obtain a copy of 7*105f6285SAndroid Build Coastguard Worker# the License at 8*105f6285SAndroid Build Coastguard Worker# 9*105f6285SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*105f6285SAndroid Build Coastguard Worker# 11*105f6285SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*105f6285SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13*105f6285SAndroid Build Coastguard Worker# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14*105f6285SAndroid Build Coastguard Worker# License for the specific language governing permissions and limitations under 15*105f6285SAndroid Build Coastguard Worker# the License. 16*105f6285SAndroid Build Coastguard Worker 17*105f6285SAndroid Build Coastguard Workerimport fnmatch 18*105f6285SAndroid Build Coastguard Workerimport glob 19*105f6285SAndroid Build Coastguard Workerimport os 20*105f6285SAndroid Build Coastguard Workerimport shutil 21*105f6285SAndroid Build Coastguard Workerimport subprocess 22*105f6285SAndroid Build Coastguard Workerimport tempfile 23*105f6285SAndroid Build Coastguard Workerfrom typing import List, Tuple 24*105f6285SAndroid Build Coastguard Workerimport zipfile 25*105f6285SAndroid Build Coastguard Worker 26*105f6285SAndroid Build Coastguard Worker 27*105f6285SAndroid Build Coastguard Workerdef unzip_otatools( 28*105f6285SAndroid Build Coastguard Worker otatools_zip_path: str, output_dir: str, patterns: List[str] = None 29*105f6285SAndroid Build Coastguard Worker) -> None: 30*105f6285SAndroid Build Coastguard Worker """Unzip otatools to a directory and set the permissions for execution. 31*105f6285SAndroid Build Coastguard Worker 32*105f6285SAndroid Build Coastguard Worker Args: 33*105f6285SAndroid Build Coastguard Worker otatools_zip_path: The path to otatools zip archive. 34*105f6285SAndroid Build Coastguard Worker output_dir: The root directory of the unzip output. 35*105f6285SAndroid Build Coastguard Worker patterns: If provided, only extract files matching any of these patterns 36*105f6285SAndroid Build Coastguard Worker from the otatools zip archive; otherwise, extract all files. 37*105f6285SAndroid Build Coastguard Worker """ 38*105f6285SAndroid Build Coastguard Worker with zipfile.ZipFile(otatools_zip_path, 'r') as zf: 39*105f6285SAndroid Build Coastguard Worker if patterns is None: 40*105f6285SAndroid Build Coastguard Worker zf.extractall(path=output_dir) 41*105f6285SAndroid Build Coastguard Worker else: 42*105f6285SAndroid Build Coastguard Worker for file in zf.namelist(): 43*105f6285SAndroid Build Coastguard Worker if any(fnmatch.fnmatch(file, p) for p in patterns): 44*105f6285SAndroid Build Coastguard Worker zf.extract(file, output_dir) 45*105f6285SAndroid Build Coastguard Worker 46*105f6285SAndroid Build Coastguard Worker for f in glob.glob(os.path.join(output_dir, 'bin', '*')): 47*105f6285SAndroid Build Coastguard Worker os.chmod(f, 0o777) 48*105f6285SAndroid Build Coastguard Worker 49*105f6285SAndroid Build Coastguard Worker 50*105f6285SAndroid Build Coastguard Workerdef _parse_copy_file_pair(copy_file_pair: str) -> Tuple[str, str]: 51*105f6285SAndroid Build Coastguard Worker """Convert a string to a source path and a destination path. 52*105f6285SAndroid Build Coastguard Worker 53*105f6285SAndroid Build Coastguard Worker Args: 54*105f6285SAndroid Build Coastguard Worker copy_file_pair: A string in the format of <src glob pattern>:<dst path>. 55*105f6285SAndroid Build Coastguard Worker 56*105f6285SAndroid Build Coastguard Worker Returns: 57*105f6285SAndroid Build Coastguard Worker The source path and the destination path. 58*105f6285SAndroid Build Coastguard Worker 59*105f6285SAndroid Build Coastguard Worker Raises: 60*105f6285SAndroid Build Coastguard Worker ValueError if the input string is in a wrong format. 61*105f6285SAndroid Build Coastguard Worker """ 62*105f6285SAndroid Build Coastguard Worker split_pair = copy_file_pair.split(':', 1) 63*105f6285SAndroid Build Coastguard Worker if len(split_pair) != 2: 64*105f6285SAndroid Build Coastguard Worker raise ValueError(f'{copy_file_pair} is not a <src>:<dst> pair.') 65*105f6285SAndroid Build Coastguard Worker src_list = glob.glob(split_pair[0]) 66*105f6285SAndroid Build Coastguard Worker if len(src_list) != 1: 67*105f6285SAndroid Build Coastguard Worker raise ValueError(f'{copy_file_pair} has more than one matched src files: ' 68*105f6285SAndroid Build Coastguard Worker f'{" ".join(src_list)}.') 69*105f6285SAndroid Build Coastguard Worker return src_list[0], split_pair[1] 70*105f6285SAndroid Build Coastguard Worker 71*105f6285SAndroid Build Coastguard Worker 72*105f6285SAndroid Build Coastguard Workerdef copy_files(copy_files_list: List[str], output_dir: str) -> None: 73*105f6285SAndroid Build Coastguard Worker """Copy files to the output directory. 74*105f6285SAndroid Build Coastguard Worker 75*105f6285SAndroid Build Coastguard Worker Args: 76*105f6285SAndroid Build Coastguard Worker copy_files_list: A list of copy file pairs, where a pair defines the src 77*105f6285SAndroid Build Coastguard Worker glob pattern and the dst path. 78*105f6285SAndroid Build Coastguard Worker output_dir: The root directory of the copy dst. 79*105f6285SAndroid Build Coastguard Worker 80*105f6285SAndroid Build Coastguard Worker Raises: 81*105f6285SAndroid Build Coastguard Worker FileExistsError if the dst file already exists. 82*105f6285SAndroid Build Coastguard Worker """ 83*105f6285SAndroid Build Coastguard Worker for pair in copy_files_list: 84*105f6285SAndroid Build Coastguard Worker src, dst = _parse_copy_file_pair(pair) 85*105f6285SAndroid Build Coastguard Worker # this line does not change dst if dst is absolute. 86*105f6285SAndroid Build Coastguard Worker dst = os.path.join(output_dir, dst) 87*105f6285SAndroid Build Coastguard Worker os.makedirs(os.path.dirname(dst), exist_ok=True) 88*105f6285SAndroid Build Coastguard Worker print(f'Copying {src} to {dst}') 89*105f6285SAndroid Build Coastguard Worker if os.path.exists(dst): 90*105f6285SAndroid Build Coastguard Worker raise FileExistsError(dst) 91*105f6285SAndroid Build Coastguard Worker shutil.copyfile(src, dst) 92*105f6285SAndroid Build Coastguard Worker 93*105f6285SAndroid Build Coastguard Worker 94*105f6285SAndroid Build Coastguard Workerdef _extract_cil_files(target_files_zip: str, output_dir: str) -> None: 95*105f6285SAndroid Build Coastguard Worker """Extract sepolicy cil files from a target files zip archive. 96*105f6285SAndroid Build Coastguard Worker 97*105f6285SAndroid Build Coastguard Worker Args: 98*105f6285SAndroid Build Coastguard Worker target_files_zip: A path to the target files zip archive. 99*105f6285SAndroid Build Coastguard Worker output_dir: The directory of extracted cil files. 100*105f6285SAndroid Build Coastguard Worker """ 101*105f6285SAndroid Build Coastguard Worker with zipfile.ZipFile(target_files_zip, 'r') as zf: 102*105f6285SAndroid Build Coastguard Worker cil_files = [name for name in zf.namelist() if name.endswith('.cil')] 103*105f6285SAndroid Build Coastguard Worker for f in cil_files: 104*105f6285SAndroid Build Coastguard Worker zf.extract(f, output_dir) 105*105f6285SAndroid Build Coastguard Worker 106*105f6285SAndroid Build Coastguard Worker 107*105f6285SAndroid Build Coastguard Workerdef _get_sepolicy_plat_version(target_files_zip: str) -> str: 108*105f6285SAndroid Build Coastguard Worker """Get the platform sepolicy version from a vendor target files zip archive. 109*105f6285SAndroid Build Coastguard Worker 110*105f6285SAndroid Build Coastguard Worker Args: 111*105f6285SAndroid Build Coastguard Worker target_files_zip: A path to the target files zip archive. 112*105f6285SAndroid Build Coastguard Worker 113*105f6285SAndroid Build Coastguard Worker Returns: 114*105f6285SAndroid Build Coastguard Worker A string that represents the platform sepolicy version. 115*105f6285SAndroid Build Coastguard Worker """ 116*105f6285SAndroid Build Coastguard Worker with zipfile.ZipFile(target_files_zip, 'r') as zf: 117*105f6285SAndroid Build Coastguard Worker try: 118*105f6285SAndroid Build Coastguard Worker with zf.open('VENDOR/etc/selinux/plat_sepolicy_vers.txt') as ver_file: 119*105f6285SAndroid Build Coastguard Worker return ver_file.readline().decode('utf-8').strip('\n') 120*105f6285SAndroid Build Coastguard Worker except Exception as error: 121*105f6285SAndroid Build Coastguard Worker print(f'cannot get platform sepolicy version from {target_files_zip}') 122*105f6285SAndroid Build Coastguard Worker raise 123*105f6285SAndroid Build Coastguard Worker 124*105f6285SAndroid Build Coastguard Worker 125*105f6285SAndroid Build Coastguard Workerdef merge_chd_sepolicy( 126*105f6285SAndroid Build Coastguard Worker framework_target_files_zip: str, vendor_target_files_zip: str, 127*105f6285SAndroid Build Coastguard Worker otatools_dir: str, output_dir: str 128*105f6285SAndroid Build Coastguard Worker) -> str: 129*105f6285SAndroid Build Coastguard Worker """Merge the sepolicy files for CHD. 130*105f6285SAndroid Build Coastguard Worker 131*105f6285SAndroid Build Coastguard Worker This function takes both the system and vendor sepolicy files from 132*105f6285SAndroid Build Coastguard Worker framework_target_files_zip, and merges them with the vendor sepolicy from 133*105f6285SAndroid Build Coastguard Worker vendor_target_files_zip to generate `chd_merged_sepolicy`. 134*105f6285SAndroid Build Coastguard Worker 135*105f6285SAndroid Build Coastguard Worker In certain instances, a device may possess components that do not put their 136*105f6285SAndroid Build Coastguard Worker sepolicy rules within the same partition as the components themselves. This 137*105f6285SAndroid Build Coastguard Worker results in a problem that CHD is missing necessary vendor sepolicy rules 138*105f6285SAndroid Build Coastguard Worker after the replacement of the device's vendor image with Cuttlefish. As a 139*105f6285SAndroid Build Coastguard Worker short term solution to resolve this issue, the vendor sepolicy files from 140*105f6285SAndroid Build Coastguard Worker framework_target_files_zip are additionally merged. 141*105f6285SAndroid Build Coastguard Worker 142*105f6285SAndroid Build Coastguard Worker Args: 143*105f6285SAndroid Build Coastguard Worker framework_target_files_zip: A path to the framework target files zip 144*105f6285SAndroid Build Coastguard Worker archive. 145*105f6285SAndroid Build Coastguard Worker vendor_target_files_zip: A path to the vendor target files zip archive. 146*105f6285SAndroid Build Coastguard Worker otatools_dir: The otatools directory. 147*105f6285SAndroid Build Coastguard Worker output_dir: The output directory for generating a merged sepolicy file. 148*105f6285SAndroid Build Coastguard Worker 149*105f6285SAndroid Build Coastguard Worker Returns: 150*105f6285SAndroid Build Coastguard Worker The path to the CHD merged sepolicy file. 151*105f6285SAndroid Build Coastguard Worker 152*105f6285SAndroid Build Coastguard Worker Raises: 153*105f6285SAndroid Build Coastguard Worker FileNotFoundError if any mandatory sepolicy file is missing. 154*105f6285SAndroid Build Coastguard Worker """ 155*105f6285SAndroid Build Coastguard Worker with tempfile.TemporaryDirectory(prefix='framework_', 156*105f6285SAndroid Build Coastguard Worker dir=output_dir) as framework_dir, \ 157*105f6285SAndroid Build Coastguard Worker tempfile.TemporaryDirectory(prefix='vendor_', 158*105f6285SAndroid Build Coastguard Worker dir=output_dir) as vendor_dir: 159*105f6285SAndroid Build Coastguard Worker merged_policy = os.path.join(output_dir, 'chd_merged_sepolicy') 160*105f6285SAndroid Build Coastguard Worker _extract_cil_files(framework_target_files_zip, framework_dir) 161*105f6285SAndroid Build Coastguard Worker _extract_cil_files(vendor_target_files_zip, vendor_dir) 162*105f6285SAndroid Build Coastguard Worker plat_ver = _get_sepolicy_plat_version(vendor_target_files_zip) 163*105f6285SAndroid Build Coastguard Worker print(f'Merging sepolicy files from {framework_target_files_zip} and ' 164*105f6285SAndroid Build Coastguard Worker f'{vendor_target_files_zip}: platform version {plat_ver}.') 165*105f6285SAndroid Build Coastguard Worker 166*105f6285SAndroid Build Coastguard Worker # (partition, path, required) 167*105f6285SAndroid Build Coastguard Worker system_policy_files = ( 168*105f6285SAndroid Build Coastguard Worker ('system', 'etc/selinux/plat_sepolicy.cil', True), 169*105f6285SAndroid Build Coastguard Worker ('system', f'etc/selinux/mapping/{plat_ver}.cil', True), 170*105f6285SAndroid Build Coastguard Worker ('system', f'etc/selinux/mapping/{plat_ver}.compat.cil', False), 171*105f6285SAndroid Build Coastguard Worker ('system_ext', 'etc/selinux/system_ext_sepolicy.cil', False), 172*105f6285SAndroid Build Coastguard Worker ('system_ext', f'etc/selinux/mapping/{plat_ver}.cil', False), 173*105f6285SAndroid Build Coastguard Worker ('system_ext', f'etc/selinux/mapping/{plat_ver}.compat.cil', False), 174*105f6285SAndroid Build Coastguard Worker ('product', 'etc/selinux/product_sepolicy.cil', False), 175*105f6285SAndroid Build Coastguard Worker ('product', f'etc/selinux/mapping/{plat_ver}.cil', False), 176*105f6285SAndroid Build Coastguard Worker ) 177*105f6285SAndroid Build Coastguard Worker vendor_policy_files = ( 178*105f6285SAndroid Build Coastguard Worker ('vendor', 'etc/selinux/vendor_sepolicy.cil', True), 179*105f6285SAndroid Build Coastguard Worker ('vendor', 'etc/selinux/plat_pub_versioned.cil', True), 180*105f6285SAndroid Build Coastguard Worker ('odm', 'etc/selinux/odm_sepolicy.cil', False), 181*105f6285SAndroid Build Coastguard Worker ) 182*105f6285SAndroid Build Coastguard Worker 183*105f6285SAndroid Build Coastguard Worker # merge system and vendor policy files from framework_dir with vendor 184*105f6285SAndroid Build Coastguard Worker # policy files from vendor_dir. 185*105f6285SAndroid Build Coastguard Worker merge_cmd = [ 186*105f6285SAndroid Build Coastguard Worker os.path.join(otatools_dir, 'bin', 'secilc'), 187*105f6285SAndroid Build Coastguard Worker '-m', '-M', 'true', '-G', '-N', 188*105f6285SAndroid Build Coastguard Worker '-o', merged_policy, 189*105f6285SAndroid Build Coastguard Worker '-f', '/dev/null' 190*105f6285SAndroid Build Coastguard Worker ] 191*105f6285SAndroid Build Coastguard Worker policy_dirs_and_files = ( 192*105f6285SAndroid Build Coastguard Worker # For the normal case, we should merge the system policies from 193*105f6285SAndroid Build Coastguard Worker # framework_dir with the vendor policies from vendor_dir. 194*105f6285SAndroid Build Coastguard Worker (framework_dir, system_policy_files), 195*105f6285SAndroid Build Coastguard Worker (vendor_dir, vendor_policy_files), 196*105f6285SAndroid Build Coastguard Worker 197*105f6285SAndroid Build Coastguard Worker # Additionally merging the vendor policies from framework_dir in order 198*105f6285SAndroid Build Coastguard Worker # to fix the policy misplaced issue. 199*105f6285SAndroid Build Coastguard Worker # TODO (b/315474132): remove this when all the policies from 200*105f6285SAndroid Build Coastguard Worker # framework_dir are moved to the right partition. 201*105f6285SAndroid Build Coastguard Worker (framework_dir, vendor_policy_files), 202*105f6285SAndroid Build Coastguard Worker ) 203*105f6285SAndroid Build Coastguard Worker for policy_dir, policy_files in policy_dirs_and_files: 204*105f6285SAndroid Build Coastguard Worker for partition, path, required in policy_files: 205*105f6285SAndroid Build Coastguard Worker policy_file = os.path.join(policy_dir, partition.upper(), path) 206*105f6285SAndroid Build Coastguard Worker if os.path.exists(policy_file): 207*105f6285SAndroid Build Coastguard Worker merge_cmd.append(policy_file) 208*105f6285SAndroid Build Coastguard Worker elif required: 209*105f6285SAndroid Build Coastguard Worker raise FileNotFoundError(f'{policy_file} does not exist') 210*105f6285SAndroid Build Coastguard Worker 211*105f6285SAndroid Build Coastguard Worker subprocess.run(merge_cmd, check=True) 212*105f6285SAndroid Build Coastguard Worker return merged_policy 213