1c1ed15f1Sopenharmony_ci#!/usr/bin/env python 2c1ed15f1Sopenharmony_ci# coding: utf-8 3c1ed15f1Sopenharmony_ci 4c1ed15f1Sopenharmony_ci""" 5c1ed15f1Sopenharmony_ciCopyright (c) 2023 Huawei Device Co., Ltd. 6c1ed15f1Sopenharmony_ciLicensed under the Apache License, Version 2.0 (the "License"); 7c1ed15f1Sopenharmony_ciyou may not use this file except in compliance with the License. 8c1ed15f1Sopenharmony_ciYou may obtain a copy of the License at 9c1ed15f1Sopenharmony_ci 10c1ed15f1Sopenharmony_ci http://www.apache.org/licenses/LICENSE-2.0 11c1ed15f1Sopenharmony_ci 12c1ed15f1Sopenharmony_ciUnless required by applicable law or agreed to in writing, software 13c1ed15f1Sopenharmony_cidistributed under the License is distributed on an "AS IS" BASIS, 14c1ed15f1Sopenharmony_ciWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15c1ed15f1Sopenharmony_ciSee the License for the specific language governing permissions and 16c1ed15f1Sopenharmony_cilimitations under the License. 17c1ed15f1Sopenharmony_ci 18c1ed15f1Sopenharmony_ci""" 19c1ed15f1Sopenharmony_ci 20c1ed15f1Sopenharmony_ciimport argparse 21c1ed15f1Sopenharmony_ciimport os 22c1ed15f1Sopenharmony_cifrom collections import defaultdict 23c1ed15f1Sopenharmony_ciimport subprocess 24c1ed15f1Sopenharmony_cifrom check_common import read_json_file, traverse_file_in_each_type 25c1ed15f1Sopenharmony_ci 26c1ed15f1Sopenharmony_ciBASELINE_SUFFIX = ".baseline" 27c1ed15f1Sopenharmony_ci 28c1ed15f1Sopenharmony_ci 29c1ed15f1Sopenharmony_ciclass PolicyDb(object): 30c1ed15f1Sopenharmony_ci def __init__(self, attributes_map, allow_map, class_map): 31c1ed15f1Sopenharmony_ci self.attributes_map = attributes_map 32c1ed15f1Sopenharmony_ci self.allow_map = allow_map 33c1ed15f1Sopenharmony_ci self.class_map = class_map 34c1ed15f1Sopenharmony_ci 35c1ed15f1Sopenharmony_ci 36c1ed15f1Sopenharmony_cidef simplify_string(string): 37c1ed15f1Sopenharmony_ci return string.replace('(', '').replace(')', '').replace('\n', '').strip() 38c1ed15f1Sopenharmony_ci 39c1ed15f1Sopenharmony_ci 40c1ed15f1Sopenharmony_cidef deal_with_allow(cil_file, allow_map, attributes_map): 41c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 42c1ed15f1Sopenharmony_ci for line in cil_read: 43c1ed15f1Sopenharmony_ci line = line.strip() 44c1ed15f1Sopenharmony_ci if not line.startswith('(allow ') and not line.startswith('(auditallow '): 45c1ed15f1Sopenharmony_ci continue 46c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 47c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 48c1ed15f1Sopenharmony_ci # (allow A B (dir (getattr))) 49c1ed15f1Sopenharmony_ci if len(elem_list) < 5: 50c1ed15f1Sopenharmony_ci continue 51c1ed15f1Sopenharmony_ci split_attribute(elem_list, allow_map, attributes_map) 52c1ed15f1Sopenharmony_ci 53c1ed15f1Sopenharmony_ci 54c1ed15f1Sopenharmony_cidef split_attribute(elem_list, allow_map, attributes_map): 55c1ed15f1Sopenharmony_ci scontext = elem_list[1] 56c1ed15f1Sopenharmony_ci tcontext = elem_list[2] 57c1ed15f1Sopenharmony_ci tclass = elem_list[3] 58c1ed15f1Sopenharmony_ci perm = elem_list[4:] 59c1ed15f1Sopenharmony_ci if scontext not in attributes_map: 60c1ed15f1Sopenharmony_ci # allow type self 61c1ed15f1Sopenharmony_ci if tcontext == 'self': 62c1ed15f1Sopenharmony_ci allow_map[scontext][(scontext, tclass)] += perm 63c1ed15f1Sopenharmony_ci # allow type attribute 64c1ed15f1Sopenharmony_ci elif tcontext in attributes_map: 65c1ed15f1Sopenharmony_ci for tcon in attributes_map[tcontext]: 66c1ed15f1Sopenharmony_ci allow_map[scontext][(tcon, tclass)] += perm 67c1ed15f1Sopenharmony_ci # allow type type 68c1ed15f1Sopenharmony_ci else: 69c1ed15f1Sopenharmony_ci allow_map[scontext][(tcontext, tclass)] += perm 70c1ed15f1Sopenharmony_ci return 71c1ed15f1Sopenharmony_ci 72c1ed15f1Sopenharmony_ci for scon in attributes_map[scontext]: 73c1ed15f1Sopenharmony_ci # allow attribute self 74c1ed15f1Sopenharmony_ci if tcontext == 'self': 75c1ed15f1Sopenharmony_ci allow_map[scon][(scon, tclass)] += perm 76c1ed15f1Sopenharmony_ci # allow attribute attribute 77c1ed15f1Sopenharmony_ci elif tcontext in attributes_map: 78c1ed15f1Sopenharmony_ci for tcon in attributes_map[tcontext]: 79c1ed15f1Sopenharmony_ci allow_map[scon][(tcon, tclass)] += perm 80c1ed15f1Sopenharmony_ci # allow attribute type 81c1ed15f1Sopenharmony_ci else: 82c1ed15f1Sopenharmony_ci allow_map[scon][(tcontext, tclass)] += perm 83c1ed15f1Sopenharmony_ci 84c1ed15f1Sopenharmony_ci 85c1ed15f1Sopenharmony_cidef deal_with_typeattributeset(cil_file, attributes_map): 86c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 87c1ed15f1Sopenharmony_ci for line in cil_read: 88c1ed15f1Sopenharmony_ci if not line.startswith('(typeattributeset '): 89c1ed15f1Sopenharmony_ci continue 90c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 91c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 92c1ed15f1Sopenharmony_ci if len(elem_list) < 3: 93c1ed15f1Sopenharmony_ci continue 94c1ed15f1Sopenharmony_ci attributes_map[elem_list[1]] += elem_list[2:] 95c1ed15f1Sopenharmony_ci 96c1ed15f1Sopenharmony_ci 97c1ed15f1Sopenharmony_cidef deal_with_class(cil_file, class_map): 98c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 99c1ed15f1Sopenharmony_ci common_map = defaultdict(list) 100c1ed15f1Sopenharmony_ci for line in cil_read: 101c1ed15f1Sopenharmony_ci if not line.startswith('(common '): 102c1ed15f1Sopenharmony_ci continue 103c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 104c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 105c1ed15f1Sopenharmony_ci if len(elem_list) < 3: 106c1ed15f1Sopenharmony_ci continue 107c1ed15f1Sopenharmony_ci common_map[elem_list[1]] += elem_list[2:] 108c1ed15f1Sopenharmony_ci 109c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 110c1ed15f1Sopenharmony_ci for line in cil_read: 111c1ed15f1Sopenharmony_ci if not line.startswith('(class '): 112c1ed15f1Sopenharmony_ci continue 113c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 114c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 115c1ed15f1Sopenharmony_ci if len(elem_list) > 2: 116c1ed15f1Sopenharmony_ci class_map[elem_list[1]] += elem_list[2:] 117c1ed15f1Sopenharmony_ci 118c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 119c1ed15f1Sopenharmony_ci for line in cil_read: 120c1ed15f1Sopenharmony_ci if not line.startswith('(classcommon '): 121c1ed15f1Sopenharmony_ci continue 122c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 123c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 124c1ed15f1Sopenharmony_ci if len(elem_list) < 3: 125c1ed15f1Sopenharmony_ci continue 126c1ed15f1Sopenharmony_ci class_map[elem_list[1]] += common_map[elem_list[2]] 127c1ed15f1Sopenharmony_ci 128c1ed15f1Sopenharmony_ci 129c1ed15f1Sopenharmony_cidef generate_database(cil_file): 130c1ed15f1Sopenharmony_ci attributes_map = defaultdict(list) 131c1ed15f1Sopenharmony_ci class_map = defaultdict(list) 132c1ed15f1Sopenharmony_ci allow_map = defaultdict(lambda: defaultdict(list)) 133c1ed15f1Sopenharmony_ci deal_with_typeattributeset(cil_file, attributes_map) 134c1ed15f1Sopenharmony_ci deal_with_allow(cil_file, allow_map, attributes_map) 135c1ed15f1Sopenharmony_ci deal_with_class(cil_file, class_map) 136c1ed15f1Sopenharmony_ci return PolicyDb(attributes_map, allow_map, class_map) 137c1ed15f1Sopenharmony_ci 138c1ed15f1Sopenharmony_ci 139c1ed15f1Sopenharmony_cidef build_conf(output_conf, file_list, with_developer=False): 140c1ed15f1Sopenharmony_ci m4_args = [] 141c1ed15f1Sopenharmony_ci if with_developer: 142c1ed15f1Sopenharmony_ci m4_args += ["-D", "build_with_developer=enable"] 143c1ed15f1Sopenharmony_ci build_conf_cmd = ["m4", "-s", "--fatal-warnings"] + m4_args + file_list 144c1ed15f1Sopenharmony_ci with open(output_conf, 'w', encoding="utf-8") as fd: 145c1ed15f1Sopenharmony_ci ret = subprocess.run(build_conf_cmd, shell=False, stdout=fd).returncode 146c1ed15f1Sopenharmony_ci if ret != 0: 147c1ed15f1Sopenharmony_ci raise Exception(ret) 148c1ed15f1Sopenharmony_ci 149c1ed15f1Sopenharmony_ci 150c1ed15f1Sopenharmony_cidef get_baseline_file(args): 151c1ed15f1Sopenharmony_ci script_path = os.path.dirname(os.path.realpath(__file__)) 152c1ed15f1Sopenharmony_ci baseline_file_list = [os.path.join(script_path, "config/glb_def.txt")] 153c1ed15f1Sopenharmony_ci baseline_file_list += traverse_file_in_each_type(args.policy_dir_list, BASELINE_SUFFIX) 154c1ed15f1Sopenharmony_ci return baseline_file_list 155c1ed15f1Sopenharmony_ci 156c1ed15f1Sopenharmony_ci 157c1ed15f1Sopenharmony_cidef generate_baseline_database(args, domain, attributes_map, with_developer): 158c1ed15f1Sopenharmony_ci baseline_file_list = get_baseline_file(args) 159c1ed15f1Sopenharmony_ci output_path = os.path.dirname(os.path.realpath(args.developer_cil_file if with_developer else args.cil_file)) 160c1ed15f1Sopenharmony_ci output_baseline = os.path.join(output_path, domain + BASELINE_SUFFIX) 161c1ed15f1Sopenharmony_ci build_conf(output_baseline, baseline_file_list, with_developer) 162c1ed15f1Sopenharmony_ci baseline_map = defaultdict(lambda: defaultdict(list)) 163c1ed15f1Sopenharmony_ci deal_with_allow(output_baseline, baseline_map, attributes_map) 164c1ed15f1Sopenharmony_ci return baseline_map[domain] 165c1ed15f1Sopenharmony_ci 166c1ed15f1Sopenharmony_ci 167c1ed15f1Sopenharmony_cidef check_baseline(args, domain, policy_db, with_developer): 168c1ed15f1Sopenharmony_ci baseline_map = generate_baseline_database(args, domain, policy_db.attributes_map, with_developer) 169c1ed15f1Sopenharmony_ci none_baseline_list = set() 170c1ed15f1Sopenharmony_ci domain_policy = policy_db.allow_map[domain] 171c1ed15f1Sopenharmony_ci baseline_diff = domain_policy.keys() ^ baseline_map.keys() 172c1ed15f1Sopenharmony_ci for diff in baseline_diff: 173c1ed15f1Sopenharmony_ci expect_perm = ''.join(['(', ' '.join(set(baseline_map.get(diff, ''))), ')))']) 174c1ed15f1Sopenharmony_ci expect = ' '.join(['expect rule: (allow', domain, ' ('.join(diff), expect_perm]) 175c1ed15f1Sopenharmony_ci actual_perm = ''.join(['(', ' '.join(set(domain_policy.get(diff, ''))), ')))']) 176c1ed15f1Sopenharmony_ci actual = ' '.join(['actual rule: (allow', domain, ' ('.join(diff), actual_perm]) 177c1ed15f1Sopenharmony_ci none_baseline_list.add('; '.join([expect, actual])) 178c1ed15f1Sopenharmony_ci 179c1ed15f1Sopenharmony_ci for contexts in domain_policy.keys(): 180c1ed15f1Sopenharmony_ci if set(baseline_map.get(contexts, '')) != set(domain_policy.get(contexts, '')): 181c1ed15f1Sopenharmony_ci expect_perm = ''.join(['(', ' '.join(set(baseline_map.get(contexts, ''))), ')))']) 182c1ed15f1Sopenharmony_ci expect = ' '.join(['expect rule: (allow', domain, ' ('.join(contexts), expect_perm]) 183c1ed15f1Sopenharmony_ci actual_perm = ''.join(['(', ' '.join(set(domain_policy.get(contexts, ''))), ')))']) 184c1ed15f1Sopenharmony_ci actual = ' '.join(['actual rule: (allow', domain, ' ('.join(contexts), actual_perm]) 185c1ed15f1Sopenharmony_ci none_baseline_list.add('; '.join([expect, actual])) 186c1ed15f1Sopenharmony_ci 187c1ed15f1Sopenharmony_ci if len(none_baseline_list): 188c1ed15f1Sopenharmony_ci print('\tcheck \'{}\' baseline in {} mode failed'.format(domain, "developer" if with_developer else "user")) 189c1ed15f1Sopenharmony_ci for violation in none_baseline_list: 190c1ed15f1Sopenharmony_ci print('\t\t{}'.format(violation)) 191c1ed15f1Sopenharmony_ci print('\tThere are two solutions:\n', 192c1ed15f1Sopenharmony_ci '\t1. Add the above actual rule to baseline file \'{}\' under \'{}\'{}\n'.format( 193c1ed15f1Sopenharmony_ci domain + BASELINE_SUFFIX, args.policy_dir_list, " and add developer_only" if with_developer else ""), 194c1ed15f1Sopenharmony_ci '\t2. Change the policy to satisfy expect rule\n') 195c1ed15f1Sopenharmony_ci return True 196c1ed15f1Sopenharmony_ci return False 197c1ed15f1Sopenharmony_ci 198c1ed15f1Sopenharmony_ci 199c1ed15f1Sopenharmony_cidef parse_args(): 200c1ed15f1Sopenharmony_ci parser = argparse.ArgumentParser() 201c1ed15f1Sopenharmony_ci parser.add_argument( 202c1ed15f1Sopenharmony_ci '--cil_file', help='the cil file path', required=True) 203c1ed15f1Sopenharmony_ci parser.add_argument( 204c1ed15f1Sopenharmony_ci '--developer_cil_file', help='the developer cil file path', required=True) 205c1ed15f1Sopenharmony_ci parser.add_argument( 206c1ed15f1Sopenharmony_ci '--config', help='the config file path', required=True) 207c1ed15f1Sopenharmony_ci parser.add_argument( 208c1ed15f1Sopenharmony_ci '--policy-dir-list', help='the policy dir list', required=True) 209c1ed15f1Sopenharmony_ci return parser.parse_args() 210c1ed15f1Sopenharmony_ci 211c1ed15f1Sopenharmony_ci 212c1ed15f1Sopenharmony_ciif __name__ == '__main__': 213c1ed15f1Sopenharmony_ci input_args = parse_args() 214c1ed15f1Sopenharmony_ci script_dir = os.path.dirname(os.path.realpath(__file__)) 215c1ed15f1Sopenharmony_ci 216c1ed15f1Sopenharmony_ci user_policy_db = generate_database(input_args.cil_file) 217c1ed15f1Sopenharmony_ci developer_policy_db = generate_database(input_args.developer_cil_file) 218c1ed15f1Sopenharmony_ci baselines = read_json_file(os.path.join(script_dir, input_args.config)).get('baseline') 219c1ed15f1Sopenharmony_ci check_result = False 220c1ed15f1Sopenharmony_ci for label_name in baselines: 221c1ed15f1Sopenharmony_ci check_result |= check_baseline(input_args, label_name, user_policy_db, False) 222c1ed15f1Sopenharmony_ci check_result |= check_baseline(input_args, label_name, developer_policy_db, True) 223c1ed15f1Sopenharmony_ci if check_result: 224c1ed15f1Sopenharmony_ci raise Exception(-1) 225