1#!/usr/bin/env python 2# coding: utf-8 3 4""" 5Copyright (c) 2021-2022 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17 18""" 19 20import os 21import argparse 22import re 23import shutil 24import subprocess 25import sys 26import platform 27from collections import defaultdict 28sys.path.append(os.path.join(os.path.dirname( 29 os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))), "build")) 30from scripts.util import build_utils 31import find 32 33 34def parse_args(): 35 parser = argparse.ArgumentParser() 36 parser.add_argument( 37 '--dst-dir', help='the output dest path', required=True) 38 parser.add_argument('--tool-path', 39 help='the sefcontext_compile bin path', required=True) 40 parser.add_argument('--policy-file', 41 help='the policy.31 file', required=True) 42 parser.add_argument('--source-root-dir', 43 help='prj root path', required=True) 44 parser.add_argument('--policy_dir_list', 45 help='policy dirs need to be included', required=True) 46 parser.add_argument('--components', 47 help='system or vendor or default', required=True) 48 parser.add_argument('--depfile', 49 help='depfile', required=True) 50 parser.add_argument('--output-file', 51 help='output file', required=True) 52 parser.add_argument('--sepolicy-dir-lists', 53 help='sepolicy dir lists', required=True) 54 return parser.parse_args() 55 56 57def run_command(in_cmd): 58 cmdstr = " ".join(in_cmd) 59 ret = subprocess.run(cmdstr, shell=True).returncode 60 if ret != 0: 61 raise Exception(ret) 62 63 64def traverse_folder_in_type(search_dir_list, file_suffix): 65 """ 66 for special folder search_dir, find all files endwith file_suffix. 67 :param search_dir: path to search 68 :param file_suffix: postfix of file name 69 :return: file list 70 """ 71 flag = 0 72 policy_file_list = [] 73 74 for item in search_dir_list: 75 for root, _, files in sorted(os.walk(item)): 76 filtered_files = [f for f in files if f.endswith(file_suffix)] 77 for each_file in filtered_files: 78 file_list_path = os.path.join(root, each_file) 79 flag |= check_contexts_file(file_list_path) 80 policy_file_list.append(file_list_path) 81 82 if flag: 83 raise Exception(flag) 84 policy_file_list.sort() 85 return " ".join(str(x) for x in policy_file_list) 86 87 88def check_contexts_file(contexts_file): 89 """ 90 Check the format of contexts file. 91 :param contexts_file: list of te file 92 :return: 93 """ 94 err = 0 95 lines = [] 96 with open(contexts_file, 'rb') as fp: 97 lines = fp.readlines() 98 if len(lines) == 0: 99 return 0 100 last_line = lines[-1] 101 if b'\n' not in last_line: 102 print("".join((contexts_file, " : need an empty line at end \n"))) 103 err = 1 104 for line in lines: 105 if line.endswith(b'\r\n') or line.endswith(b'\r'): 106 print("".join((contexts_file, " : must be unix format\n"))) 107 err = 1 108 break 109 return err 110 111 112def combine_contexts_file(file_contexts_list, combined_file_contexts): 113 cat_cmd = ["cat", 114 file_contexts_list, 115 ">", combined_file_contexts + "_tmp"] 116 run_command(cat_cmd) 117 118 grep_cmd = ["grep -v ^#", 119 combined_file_contexts + "_tmp", 120 "| grep -v ^$", 121 ">", combined_file_contexts] 122 run_command(grep_cmd) 123 124 125def check_redefinition(contexts_file): 126 type_hash = defaultdict(list) 127 err = 0 128 with open(contexts_file, 'r') as contexts_read: 129 pattern = re.compile(r'(\S+)\s+u:object_r:\S+:s0') 130 line_index = 0 131 for line in contexts_read: 132 line_ = line.lstrip() 133 line_index += 1 134 if line_.startswith('#') or line_.strip() == '': 135 continue 136 match = pattern.match(line_) 137 if match: 138 type_hash[match.group(1)].append(line_index) 139 else: 140 print(contexts_file + ":" + 141 str(line_index) + " format check fail") 142 err = 1 143 contexts_read.close() 144 if err: 145 print("***********************************************************") 146 print("please check whether the format meets the following rules:") 147 print("[required format]: * u:object_r:*:s0") 148 print("***********************************************************") 149 raise Exception(err) 150 err = 0 151 for type_key in type_hash.keys(): 152 if len(type_hash[type_key]) > 1: 153 err = 1 154 str_seq = (contexts_file, ":") 155 err_msg = "".join(str_seq) 156 for linenum in type_hash[type_key]: 157 str_seq = (err_msg, str(linenum), ":") 158 err_msg = "".join(str_seq) 159 str_seq = (err_msg, "'type ", str(type_key), " is redefinition'") 160 err_msg = "".join(str_seq) 161 print(err_msg) 162 if err: 163 raise Exception(err) 164 165 166def check_common_contexts(args, contexts_file): 167 """ 168 check whether context used in contexts_file is defined in policy.31. 169 :param args: 170 :param contexts_file: path of contexts file 171 :return: 172 """ 173 check_redefinition(contexts_file) 174 175 check_cmd = [os.path.join(args.tool_path, "sefcontext_compile"), 176 "-o", contexts_file + ".bin", 177 "-p", args.policy_file, 178 contexts_file] 179 run_command(check_cmd) 180 if os.path.exists(contexts_file + ".bin"): 181 os.unlink(contexts_file + ".bin") 182 183 184def echo_error(): 185 print("***********************************************************") 186 print("please check whether the format meets the following rules:") 187 print("[required format]: apl=* [name=*] [debuggable=*] [extension=*] [extra=*] domain=* type=*") 188 print("apl=*, apl should be one of system_core|system_basic|normal") 189 print("name=*, name is 'optional'") 190 print("debuggable=*, debuggable is 'optional'") 191 print("extension=*, extension is 'optional'") 192 print("extra=*, extra is 'optional'") 193 print("domain=*, hapdomain selinux type") 194 print("type=*, hapdatafile selinux type") 195 print("***********************************************************") 196 197 198def sehap_check_line(line, line_index, contexts_write, domain, contexts_file): 199 line_ = line.lstrip() 200 if line_.startswith('#') or line_.strip() == '': 201 contexts_write.write(line) 202 return 203 204 pattern = re.compile( 205 r'apl=(system_core|system_basic|normal)\s+' 206 r'(debuggable=\S+\s+)?' 207 r'(name=\S+\s+)?' 208 r'(extra=\S+\s+)?' 209 r'(extension=\S+\s+)?' 210 r'domain=(\S+)\s+' 211 r'type=(\S+)\s*' 212 r'.*\n', 213 re.DOTALL 214 ) 215 match = pattern.match(line_) 216 if match: 217 if domain: 218 line = match.group(1) + " u:r:" + match.group(6) + ":s0\n" 219 else: 220 line = match.group(1) + " u:object_r:" + match.group(7) + ":s0\n" 221 contexts_write.write(line) 222 else: 223 print(contexts_file + ":" + str(line_index) + " format check fail") 224 raise Exception(1) 225 226 227def check_sehap_contexts(args, contexts_file, domain): 228 """ 229 check domain or type defined in sehap_contexts. 230 :param args: 231 :param contexts_file: path of contexts file 232 :param domain: true for domain, false for type 233 :return: 234 """ 235 shutil.copyfile(contexts_file, contexts_file + "_bk") 236 try: 237 with open(contexts_file + "_bk", 'r') as contexts_read, open(contexts_file, 'w') as contexts_write: 238 line_index = 0 239 for line in contexts_read: 240 line_index += 1 241 sehap_check_line(line, line_index, contexts_write, domain, contexts_file) 242 except Exception as e: 243 shutil.move(contexts_file + "_bk", contexts_file) 244 echo_error() 245 raise e 246 247 check_cmd = [os.path.join(args.tool_path, "sefcontext_compile"), 248 "-o", contexts_file + ".bin", 249 "-p", args.policy_file, 250 contexts_file] 251 ret = subprocess.run(" ".join(check_cmd), shell=True).returncode 252 253 if ret != 0: 254 shutil.move(contexts_file + "_bk", contexts_file) 255 raise Exception(ret) 256 257 shutil.move(contexts_file + "_bk", contexts_file) 258 259 if os.path.exists(contexts_file + ".bin"): 260 os.unlink(contexts_file + ".bin") 261 262 263def build_file_contexts(args, output_path, policy_path, all_policy_path): 264 if args.components == "default": 265 all_combined_file_contexts = os.path.join(output_path, "file_contexts") 266 else: 267 all_combined_file_contexts = os.path.join(output_path, "all_file_contexts") 268 file_contexts_list = traverse_folder_in_type(policy_path, "file_contexts") 269 combined_file_contexts = os.path.join(output_path, "file_contexts") 270 combine_contexts_file(file_contexts_list, combined_file_contexts) 271 272 all_file_contexts_list = traverse_folder_in_type( 273 all_policy_path, "file_contexts") 274 combine_contexts_file(all_file_contexts_list, all_combined_file_contexts) 275 276 check_redefinition(all_combined_file_contexts) 277 278 build_bin_cmd = [os.path.join(args.tool_path, "sefcontext_compile"), 279 "-o", os.path.join(args.dst_dir, "file_contexts.bin"), 280 "-p", args.policy_file, 281 all_combined_file_contexts] 282 run_command(build_bin_cmd) 283 284 285def build_common_contexts(args, output_path, contexts_file_name, policy_path, all_policy_path): 286 if args.components == "default": 287 all_combined_contexts = output_path + contexts_file_name 288 else: 289 all_combined_contexts = output_path + "all_" + contexts_file_name 290 contexts_list = traverse_folder_in_type(policy_path, contexts_file_name) 291 combined_contexts = output_path + contexts_file_name 292 combine_contexts_file(contexts_list, combined_contexts) 293 294 all_contexts_list = traverse_folder_in_type(all_policy_path, contexts_file_name) 295 combine_contexts_file(all_contexts_list, all_combined_contexts) 296 check_common_contexts(args, all_combined_contexts) 297 298 299def build_sehap_contexts(args, output_path, policy_path): 300 contexts_list = traverse_folder_in_type( 301 policy_path, "sehap_contexts") 302 303 combined_contexts = os.path.join(output_path, "sehap_contexts") 304 combine_contexts_file(contexts_list, combined_contexts) 305 306 check_sehap_contexts(args, combined_contexts, 1) 307 check_sehap_contexts(args, combined_contexts, 0) 308 309 310def prepare_build_path(dir_list, root_dir, build_dir_list): 311 build_contexts_list = \ 312 ["base/security/selinux_adapter/sepolicy/base", "base/security/selinux_adapter/sepolicy/ohos_policy"] 313 build_contexts_list += dir_list.split(":") 314 315 for i in build_contexts_list: 316 if i == "" or i == "default": 317 continue 318 path = os.path.join(root_dir, i) 319 if (os.path.exists(path)): 320 build_dir_list.append(path) 321 else: 322 print("following path not exists!! {}".format(path)) 323 exit(-1) 324 325 326def traverse_folder_in_dir_name(search_dir, folder_suffix): 327 folder_list = [] 328 for root, dirs, _ in sorted(os.walk(search_dir)): 329 for dir_i in dirs: 330 if dir_i == folder_suffix: 331 folder_list.append(os.path.join(root, dir_i)) 332 return folder_list 333 334 335def main(args): 336 if sys.platform == "linux" and platform.machine().lower() == "aarch64": 337 libpcre2_path = os.path.realpath("./clang_arm64/thirdparty/pcre2/") 338 else: 339 libpcre2_path = os.path.realpath("./clang_x64/thirdparty/pcre2/") 340 os.environ['LD_LIBRARY_PATH'] = libpcre2_path 341 output_path = args.dst_dir 342 policy_path = [] 343 prepare_build_path(args.policy_dir_list, args.source_root_dir, policy_path) 344 345 public_policy = [] 346 system_policy = [] 347 vendor_policy = [] 348 349 for item in policy_path: 350 public_policy += traverse_folder_in_dir_name(item, "public") 351 system_policy += traverse_folder_in_dir_name(item, "system") 352 vendor_policy += traverse_folder_in_dir_name(item, "vendor") 353 354 system_folder_list = public_policy + system_policy 355 vendor_folder_list = public_policy + vendor_policy 356 all_folder_list = public_policy + system_policy + vendor_policy 357 358 folder_list = [] 359 360 if args.components == "system": 361 folder_list = system_folder_list 362 elif args.components == "vendor": 363 folder_list = vendor_folder_list 364 else: 365 folder_list = all_folder_list 366 367 build_file_contexts(args, output_path, folder_list, all_folder_list) 368 build_common_contexts(args, output_path, "service_contexts", folder_list, all_folder_list) 369 build_common_contexts(args, output_path, "hdf_service_contexts", folder_list, all_folder_list) 370 build_common_contexts(args, output_path, "parameter_contexts", folder_list, all_folder_list) 371 build_sehap_contexts(args, output_path, all_folder_list) 372 373 374if __name__ == "__main__": 375 input_args = parse_args() 376 if input_args.depfile: 377 dep_file = find.get_all_sepolicy_file(input_args.sepolicy_dir_lists) 378 dep_file.sort() 379 build_utils.write_depfile(input_args.depfile, input_args.output_file, dep_file, add_pydeps=False) 380 main(input_args) 381