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 25from collections import defaultdict 26 27 28def parse_args(): 29 parser = argparse.ArgumentParser() 30 parser.add_argument( 31 '--dst-dir', help='the output dest path', required=True) 32 parser.add_argument('--tool-path', 33 help='the sefcontext_compile bin path', required=True) 34 parser.add_argument('--policy-file', 35 help='the policy.31 file', required=True) 36 parser.add_argument('--source-root-dir', 37 help='prj root path', required=True) 38 parser.add_argument('--policy_dir_list', 39 help='policy dirs need to be included', required=True) 40 return parser.parse_args() 41 42 43def run_command(in_cmd): 44 cmdstr = " ".join(in_cmd) 45 ret = subprocess.run(cmdstr, shell=True).returncode 46 if ret != 0: 47 raise Exception(ret) 48 49 50def traverse_folder_in_type(search_dir_list, file_suffix): 51 """ 52 for special folder search_dir, find all files endwith file_suffix. 53 :param search_dir: path to search 54 :param file_suffix: postfix of file name 55 :return: file list 56 """ 57 58 policy_file_list = [] 59 for item in search_dir_list: 60 for root, _, files in os.walk(item): 61 for each_file in files: 62 file_name = os.path.basename(each_file) 63 if file_name == file_suffix: 64 policy_file_list.append(os.path.join(root, each_file)) 65 policy_file_list.sort() 66 return " ".join(str(x) for x in policy_file_list) 67 68 69def combine_contexts_file(file_contexts_list, combined_file_contexts): 70 cat_cmd = ["cat", 71 file_contexts_list, 72 ">", combined_file_contexts + "_tmp"] 73 run_command(cat_cmd) 74 75 grep_cmd = ["grep -v ^#", 76 combined_file_contexts + "_tmp", 77 "| grep -v ^$", 78 ">", combined_file_contexts] 79 run_command(grep_cmd) 80 81 82def check_redefinition(contexts_file): 83 type_hash = defaultdict(list) 84 err = 0 85 with open(contexts_file, 'r') as contexts_read: 86 pattern = re.compile(r'(\S+)\s+u:object_r:\S+:s0') 87 line_index = 0 88 for line in contexts_read: 89 line_ = line.lstrip() 90 line_index += 1 91 if line_.startswith('#') or line_.strip() == '': 92 continue 93 match = pattern.match(line_) 94 if match: 95 type_hash[match.group(1)].append(line_index) 96 else: 97 print(contexts_file + ":" + 98 str(line_index) + " format check fail") 99 err = 1 100 contexts_read.close() 101 if err: 102 print("***********************************************************") 103 print("please check whether the format meets the following rules:") 104 print("[required format]: * u:object_r:*:s0") 105 print("***********************************************************") 106 raise Exception(err) 107 err = 0 108 for type_key in type_hash.keys(): 109 if len(type_hash[type_key]) > 1: 110 err = 1 111 str_seq = (contexts_file, ":") 112 err_msg = "".join(str_seq) 113 for linenum in type_hash[type_key]: 114 str_seq = (err_msg, str(linenum), ":") 115 err_msg = "".join(str_seq) 116 str_seq = (err_msg, "'type ", str(type_key), " is redefinition'") 117 err_msg = "".join(str_seq) 118 print(err_msg) 119 if err: 120 raise Exception(err) 121 122 123def check_common_contexts(args, contexts_file): 124 """ 125 check whether context used in contexts_file is defined in policy.31. 126 :param args: 127 :param contexts_file: path of contexts file 128 :return: 129 """ 130 check_redefinition(contexts_file) 131 132 check_cmd = [os.path.join(args.tool_path, "sefcontext_compile"), 133 "-o", contexts_file + ".bin", 134 "-p", args.policy_file, 135 contexts_file] 136 run_command(check_cmd) 137 if os.path.exists(contexts_file + ".bin"): 138 os.unlink(contexts_file + ".bin") 139 140 141def check_sehap_contexts(args, contexts_file, domain): 142 """ 143 check domain or type defined in sehap_contexts. 144 :param args: 145 :param contexts_file: path of contexts file 146 :param domain: true for domain, false for type 147 :return: 148 """ 149 shutil.copyfile(contexts_file, contexts_file + "_bk") 150 err = 0 151 with open(contexts_file + "_bk", 'r') as contexts_read, open(contexts_file, 'w') as contexts_write: 152 pattern = re.compile( 153 r'apl=(system_core|system_basic|normal)\s+(name=\S+\s+)?domain=(\S+)\s+type=(\S+)\s*\n') 154 line_index = 0 155 for line in contexts_read: 156 line_ = line.lstrip() 157 line_index += 1 158 if line_.startswith('#') or line_.strip() == '': 159 contexts_write.write(line) 160 continue 161 match = pattern.match(line_) 162 if match: 163 if match.group(1) == 'normal' and match.group(2) != None: 164 print(contexts_file + ":" + 165 str(line_index) + " name cannot be set while apl=normal") 166 err = 1 167 if domain: 168 line = match.group(1) + " u:r:" + match.group(3) + ":s0\n" 169 else: 170 line = match.group(1) + " u:object_r:" + \ 171 match.group(4) + ":s0\n" 172 contexts_write.write(line) 173 else: 174 print(contexts_file + ":" + 175 str(line_index) + " format check fail") 176 err = 1 177 contexts_read.close() 178 contexts_write.close() 179 if err: 180 shutil.move(contexts_file + "_bk", contexts_file) 181 print("***********************************************************") 182 print("please check whether the format meets the following rules:") 183 print("[required format]: apl=* name=* domain=* type=*") 184 print("apl=*, apl should be one of system_core|system_basic|normal") 185 print("name=*, name is 'optional'") 186 print("domain=*, hapdomain selinux type") 187 print("type=*, hapdatafile selinux type") 188 print("***********************************************************") 189 raise Exception(err) 190 check_cmd = [os.path.join(args.tool_path, "sefcontext_compile"), 191 "-o", contexts_file + ".bin", 192 "-p", args.policy_file, 193 contexts_file] 194 ret = subprocess.run(" ".join(check_cmd), shell=True).returncode 195 if ret != 0: 196 shutil.move(contexts_file + "_bk", contexts_file) 197 raise Exception(ret) 198 shutil.move(contexts_file + "_bk", contexts_file) 199 if os.path.exists(contexts_file + ".bin"): 200 os.unlink(contexts_file + ".bin") 201 202 203def build_file_contexts(args, output_path, policy_path): 204 file_contexts_list = traverse_folder_in_type( 205 policy_path, "file_contexts") 206 207 combined_file_contexts = os.path.join(output_path, "file_contexts") 208 combine_contexts_file(file_contexts_list, combined_file_contexts) 209 210 build_tmp_cmd = ["m4", 211 "--fatal-warnings", 212 "-s", combined_file_contexts, ">", os.path.join(output_path, "file_contexts.tmp")] 213 run_command(build_tmp_cmd) 214 215 check_redefinition(combined_file_contexts) 216 217 build_bin_cmd = [os.path.join(args.tool_path, "sefcontext_compile"), 218 "-o", os.path.join(args.dst_dir, "file_contexts.bin"), 219 "-p", args.policy_file, 220 os.path.join(output_path, "file_contexts.tmp")] 221 run_command(build_bin_cmd) 222 223 224def build_common_contexts(args, output_path, contexts_file_name, policy_path): 225 226 contexts_list = traverse_folder_in_type( 227 policy_path, contexts_file_name) 228 229 combined_contexts = output_path + contexts_file_name 230 combine_contexts_file(contexts_list, combined_contexts) 231 232 check_common_contexts(args, combined_contexts) 233 234 235def build_sehap_contexts(args, output_path, policy_path): 236 237 contexts_list = traverse_folder_in_type( 238 policy_path, "sehap_contexts") 239 240 combined_contexts = os.path.join(output_path, "sehap_contexts") 241 combine_contexts_file(contexts_list, combined_contexts) 242 243 check_sehap_contexts(args, combined_contexts, 1) 244 check_sehap_contexts(args, combined_contexts, 0) 245 246 247def prepare_build_path(dir_list, root_dir, build_dir_list): 248 249 build_contexts_list = ["base/security/selinux/sepolicy/base", "base/security/selinux/sepolicy/ohos_policy"] 250 build_contexts_list += dir_list.split(":") 251 252 for i in build_contexts_list: 253 if i == "" or i == "default": 254 continue 255 path = os.path.join(root_dir, i) 256 if (os.path.exists(path)): 257 build_dir_list.append(path) 258 else: 259 print("following path not exists!! {}".format(path)) 260 exit(-1) 261 262 263def main(args): 264 265 output_path = args.dst_dir 266 267 policy_path = [] 268 prepare_build_path(args.policy_dir_list, args.source_root_dir, policy_path) 269 270 build_file_contexts(args, output_path, policy_path) 271 build_common_contexts(args, output_path, "service_contexts", policy_path) 272 build_common_contexts(args, output_path, "hdf_service_contexts", policy_path) 273 build_common_contexts(args, output_path, "parameter_contexts", policy_path) 274 build_sehap_contexts(args, output_path, policy_path) 275 276 277if __name__ == "__main__": 278 input_args = parse_args() 279 main(input_args) 280