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_cifrom check_common import read_json_file, traverse_file_in_each_type 24c1ed15f1Sopenharmony_ci 25c1ed15f1Sopenharmony_ciWHITELIST_FILE_NAME = "perm_group_whitelist.json" 26c1ed15f1Sopenharmony_ci 27c1ed15f1Sopenharmony_ci 28c1ed15f1Sopenharmony_ciclass PolicyDb(object): 29c1ed15f1Sopenharmony_ci def __init__(self, attributes_map, allow_map, class_map): 30c1ed15f1Sopenharmony_ci self.attributes_map = attributes_map 31c1ed15f1Sopenharmony_ci self.allow_map = allow_map 32c1ed15f1Sopenharmony_ci self.class_map = class_map 33c1ed15f1Sopenharmony_ci 34c1ed15f1Sopenharmony_ci 35c1ed15f1Sopenharmony_cidef simplify_string(string): 36c1ed15f1Sopenharmony_ci return string.replace('(', '').replace(')', '').replace('\n', '').strip() 37c1ed15f1Sopenharmony_ci 38c1ed15f1Sopenharmony_ci 39c1ed15f1Sopenharmony_cidef deal_with_allow(cil_file, allow_map, attributes_map): 40c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 41c1ed15f1Sopenharmony_ci for line in cil_read: 42c1ed15f1Sopenharmony_ci if not line.startswith('(allow ') and not line.startswith('(auditallow '): 43c1ed15f1Sopenharmony_ci continue 44c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 45c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 46c1ed15f1Sopenharmony_ci # (allow A B (dir (getattr))) 47c1ed15f1Sopenharmony_ci if len(elem_list) < 5: 48c1ed15f1Sopenharmony_ci continue 49c1ed15f1Sopenharmony_ci split_attribute(elem_list, allow_map, attributes_map) 50c1ed15f1Sopenharmony_ci 51c1ed15f1Sopenharmony_ci 52c1ed15f1Sopenharmony_cidef split_attribute(elem_list, allow_map, attributes_map): 53c1ed15f1Sopenharmony_ci scontext = elem_list[1] 54c1ed15f1Sopenharmony_ci tcontext = elem_list[2] 55c1ed15f1Sopenharmony_ci tclass = elem_list[3] 56c1ed15f1Sopenharmony_ci perm = elem_list[4:] 57c1ed15f1Sopenharmony_ci if scontext not in attributes_map: 58c1ed15f1Sopenharmony_ci # allow type self 59c1ed15f1Sopenharmony_ci if tcontext == 'self': 60c1ed15f1Sopenharmony_ci allow_map[(scontext, scontext)][tclass] += perm 61c1ed15f1Sopenharmony_ci # allow type attribute 62c1ed15f1Sopenharmony_ci elif tcontext in attributes_map: 63c1ed15f1Sopenharmony_ci for tcon in attributes_map[tcontext]: 64c1ed15f1Sopenharmony_ci allow_map[(scontext, tcon)][tclass] += perm 65c1ed15f1Sopenharmony_ci # allow type type 66c1ed15f1Sopenharmony_ci else: 67c1ed15f1Sopenharmony_ci allow_map[(scontext, tcontext)][tclass] += perm 68c1ed15f1Sopenharmony_ci return 69c1ed15f1Sopenharmony_ci 70c1ed15f1Sopenharmony_ci for scon in attributes_map[scontext]: 71c1ed15f1Sopenharmony_ci # allow attribute self 72c1ed15f1Sopenharmony_ci if tcontext == 'self': 73c1ed15f1Sopenharmony_ci allow_map[(scon, scon)][tclass] += perm 74c1ed15f1Sopenharmony_ci # allow attribute attribute 75c1ed15f1Sopenharmony_ci elif tcontext in attributes_map: 76c1ed15f1Sopenharmony_ci for tcon in attributes_map[tcontext]: 77c1ed15f1Sopenharmony_ci allow_map[(scon, tcon)][tclass] += perm 78c1ed15f1Sopenharmony_ci # allow attribute type 79c1ed15f1Sopenharmony_ci else: 80c1ed15f1Sopenharmony_ci allow_map[(scon, tcontext)][tclass] += perm 81c1ed15f1Sopenharmony_ci 82c1ed15f1Sopenharmony_ci 83c1ed15f1Sopenharmony_cidef deal_with_typeattributeset(cil_file, attributes_map): 84c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 85c1ed15f1Sopenharmony_ci for line in cil_read: 86c1ed15f1Sopenharmony_ci if not line.startswith('(typeattributeset '): 87c1ed15f1Sopenharmony_ci continue 88c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 89c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 90c1ed15f1Sopenharmony_ci if len(elem_list) < 3: 91c1ed15f1Sopenharmony_ci continue 92c1ed15f1Sopenharmony_ci attributes_map[elem_list[1]] += elem_list[2:] 93c1ed15f1Sopenharmony_ci 94c1ed15f1Sopenharmony_ci 95c1ed15f1Sopenharmony_cidef deal_with_class(cil_file, class_map): 96c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 97c1ed15f1Sopenharmony_ci common_map = defaultdict(list) 98c1ed15f1Sopenharmony_ci for line in cil_read: 99c1ed15f1Sopenharmony_ci if not line.startswith('(common '): 100c1ed15f1Sopenharmony_ci continue 101c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 102c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 103c1ed15f1Sopenharmony_ci if len(elem_list) < 3: 104c1ed15f1Sopenharmony_ci continue 105c1ed15f1Sopenharmony_ci common_map[elem_list[1]] += elem_list[2:] 106c1ed15f1Sopenharmony_ci 107c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 108c1ed15f1Sopenharmony_ci for line in cil_read: 109c1ed15f1Sopenharmony_ci if not line.startswith('(class '): 110c1ed15f1Sopenharmony_ci continue 111c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 112c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 113c1ed15f1Sopenharmony_ci if len(elem_list) > 2: 114c1ed15f1Sopenharmony_ci class_map[elem_list[1]] += elem_list[2:] 115c1ed15f1Sopenharmony_ci 116c1ed15f1Sopenharmony_ci with open(cil_file, 'r', encoding='utf-8') as cil_read: 117c1ed15f1Sopenharmony_ci for line in cil_read: 118c1ed15f1Sopenharmony_ci if not line.startswith('(classcommon '): 119c1ed15f1Sopenharmony_ci continue 120c1ed15f1Sopenharmony_ci sub_string = simplify_string(line) 121c1ed15f1Sopenharmony_ci elem_list = sub_string.split(' ') 122c1ed15f1Sopenharmony_ci if len(elem_list) < 3: 123c1ed15f1Sopenharmony_ci continue 124c1ed15f1Sopenharmony_ci class_map[elem_list[1]] += common_map[elem_list[2]] 125c1ed15f1Sopenharmony_ci 126c1ed15f1Sopenharmony_ci 127c1ed15f1Sopenharmony_cidef generate_database(cil_file): 128c1ed15f1Sopenharmony_ci attributes_map = defaultdict(list) 129c1ed15f1Sopenharmony_ci class_map = defaultdict(list) 130c1ed15f1Sopenharmony_ci allow_map = defaultdict(lambda: defaultdict(list)) 131c1ed15f1Sopenharmony_ci deal_with_typeattributeset(cil_file, attributes_map) 132c1ed15f1Sopenharmony_ci deal_with_allow(cil_file, allow_map, attributes_map) 133c1ed15f1Sopenharmony_ci deal_with_class(cil_file, class_map) 134c1ed15f1Sopenharmony_ci return PolicyDb(attributes_map, allow_map, class_map) 135c1ed15f1Sopenharmony_ci 136c1ed15f1Sopenharmony_ci 137c1ed15f1Sopenharmony_ciclass CheckPermGroup(object): 138c1ed15f1Sopenharmony_ci def __init__(self, check_class_list, check_perms): 139c1ed15f1Sopenharmony_ci self.check_class_list = check_class_list 140c1ed15f1Sopenharmony_ci self.check_perms = check_perms 141c1ed15f1Sopenharmony_ci 142c1ed15f1Sopenharmony_ci 143c1ed15f1Sopenharmony_cidef get_check_class_list(check_tclass, check_perms, class_map): 144c1ed15f1Sopenharmony_ci if check_tclass == '*': 145c1ed15f1Sopenharmony_ci check_class_list = [] 146c1ed15f1Sopenharmony_ci for tclass in class_map.keys(): 147c1ed15f1Sopenharmony_ci if set(check_perms) <= set(class_map[tclass]): 148c1ed15f1Sopenharmony_ci check_class_list.append(tclass) 149c1ed15f1Sopenharmony_ci return check_class_list 150c1ed15f1Sopenharmony_ci return [check_tclass] 151c1ed15f1Sopenharmony_ci 152c1ed15f1Sopenharmony_ci 153c1ed15f1Sopenharmony_cidef get_perm_group_list(rule, class_map): 154c1ed15f1Sopenharmony_ci check_perm_group_list = [] 155c1ed15f1Sopenharmony_ci perm_group_list = rule.get('perm_group') 156c1ed15f1Sopenharmony_ci for perm_group in perm_group_list: 157c1ed15f1Sopenharmony_ci check_tclass = perm_group.get('tclass') 158c1ed15f1Sopenharmony_ci check_perms = perm_group.get('perm').split(' ') 159c1ed15f1Sopenharmony_ci check_class_list = get_check_class_list(check_tclass, check_perms, class_map) 160c1ed15f1Sopenharmony_ci check_perm_group_list.append(CheckPermGroup(check_class_list, check_perms)) 161c1ed15f1Sopenharmony_ci return check_perm_group_list 162c1ed15f1Sopenharmony_ci 163c1ed15f1Sopenharmony_ci 164c1ed15f1Sopenharmony_cidef get_whitelist(args, check_name, with_developer): 165c1ed15f1Sopenharmony_ci whitelist_file_list = traverse_file_in_each_type(args.policy_dir_list, WHITELIST_FILE_NAME) 166c1ed15f1Sopenharmony_ci contexts_list = [] 167c1ed15f1Sopenharmony_ci for path in whitelist_file_list: 168c1ed15f1Sopenharmony_ci white_list = read_json_file(path).get('whitelist') 169c1ed15f1Sopenharmony_ci for item in white_list: 170c1ed15f1Sopenharmony_ci if item.get('name') != check_name: 171c1ed15f1Sopenharmony_ci continue 172c1ed15f1Sopenharmony_ci contexts_list.extend(item.get('user')) 173c1ed15f1Sopenharmony_ci if with_developer: 174c1ed15f1Sopenharmony_ci contexts_list.extend(item.get('developer')) 175c1ed15f1Sopenharmony_ci return contexts_list 176c1ed15f1Sopenharmony_ci 177c1ed15f1Sopenharmony_ci 178c1ed15f1Sopenharmony_cidef check_perm_group(args, rule, policy_db, with_developer): 179c1ed15f1Sopenharmony_ci check_name = rule.get('name') 180c1ed15f1Sopenharmony_ci check_perm_group_list = get_perm_group_list(rule, policy_db.class_map) 181c1ed15f1Sopenharmony_ci contexts_list = get_whitelist(args, check_name, with_developer) 182c1ed15f1Sopenharmony_ci 183c1ed15f1Sopenharmony_ci non_exempt_violator_list = [] 184c1ed15f1Sopenharmony_ci violator_list = [] 185c1ed15f1Sopenharmony_ci for contexts in policy_db.allow_map.keys(): 186c1ed15f1Sopenharmony_ci check_result = 0 187c1ed15f1Sopenharmony_ci for perm_group in check_perm_group_list: 188c1ed15f1Sopenharmony_ci check_success = False 189c1ed15f1Sopenharmony_ci for check_class in perm_group.check_class_list: 190c1ed15f1Sopenharmony_ci check_success |= (set(perm_group.check_perms) <= set(policy_db.allow_map[contexts][check_class])) 191c1ed15f1Sopenharmony_ci if check_success: 192c1ed15f1Sopenharmony_ci check_result += 1 193c1ed15f1Sopenharmony_ci if check_result != len(check_perm_group_list): 194c1ed15f1Sopenharmony_ci continue 195c1ed15f1Sopenharmony_ci violater = ' '.join(contexts) 196c1ed15f1Sopenharmony_ci # all violation list 197c1ed15f1Sopenharmony_ci violator_list.append(violater) 198c1ed15f1Sopenharmony_ci # if not in whitelist 199c1ed15f1Sopenharmony_ci if violater not in contexts_list: 200c1ed15f1Sopenharmony_ci non_exempt_violator_list.append(violater) 201c1ed15f1Sopenharmony_ci 202c1ed15f1Sopenharmony_ci if len(non_exempt_violator_list): 203c1ed15f1Sopenharmony_ci print('\tcheck rule \'{}\' in {} mode failed, {}'.format( 204c1ed15f1Sopenharmony_ci check_name, "developer" if with_developer else "user", rule.get('description'))) 205c1ed15f1Sopenharmony_ci print('\tviolation list (scontext tcontext):') 206c1ed15f1Sopenharmony_ci for violation in non_exempt_violator_list: 207c1ed15f1Sopenharmony_ci print('\t\t{}'.format(violation)) 208c1ed15f1Sopenharmony_ci print('\tThere are two solutions:\n', 209c1ed15f1Sopenharmony_ci '\t1. Add the above list to whitelist file \'{}\' under \'{}\' in \'{}\' part of \'{}\'\n'.format( 210c1ed15f1Sopenharmony_ci WHITELIST_FILE_NAME, args.policy_dir_list, "developer" if with_developer else "user", check_name), 211c1ed15f1Sopenharmony_ci '\t2. Change the policy to avoid violating rule \'{}\'\n'.format(check_name)) 212c1ed15f1Sopenharmony_ci return True 213c1ed15f1Sopenharmony_ci 214c1ed15f1Sopenharmony_ci diff_list = list(set(contexts_list) - set(violator_list)) 215c1ed15f1Sopenharmony_ci if len(diff_list): 216c1ed15f1Sopenharmony_ci print('\tcheck rule \'{}\' failed in whitelist file \'{}\'\n'.format(check_name, WHITELIST_FILE_NAME), 217c1ed15f1Sopenharmony_ci '\tremove the following unnecessary whitelists in rule \'{}\' part \'{}\':'.format( 218c1ed15f1Sopenharmony_ci check_name, 'developer' if with_developer else 'user')) 219c1ed15f1Sopenharmony_ci for diff in diff_list: 220c1ed15f1Sopenharmony_ci print('\t\t{}'.format(diff)) 221c1ed15f1Sopenharmony_ci return True 222c1ed15f1Sopenharmony_ci return False 223c1ed15f1Sopenharmony_ci 224c1ed15f1Sopenharmony_ci 225c1ed15f1Sopenharmony_cidef parse_args(): 226c1ed15f1Sopenharmony_ci parser = argparse.ArgumentParser() 227c1ed15f1Sopenharmony_ci parser.add_argument( 228c1ed15f1Sopenharmony_ci '--cil_file', help='the cil file path', required=True) 229c1ed15f1Sopenharmony_ci parser.add_argument( 230c1ed15f1Sopenharmony_ci '--developer_cil_file', help='the developer cil file path', required=True) 231c1ed15f1Sopenharmony_ci parser.add_argument( 232c1ed15f1Sopenharmony_ci '--policy-dir-list', help='the whitelist path list', required=True) 233c1ed15f1Sopenharmony_ci parser.add_argument( 234c1ed15f1Sopenharmony_ci '--config', help='the config file path', required=True) 235c1ed15f1Sopenharmony_ci return parser.parse_args() 236c1ed15f1Sopenharmony_ci 237c1ed15f1Sopenharmony_ci 238c1ed15f1Sopenharmony_ciif __name__ == '__main__': 239c1ed15f1Sopenharmony_ci input_args = parse_args() 240c1ed15f1Sopenharmony_ci script_path = os.path.dirname(os.path.realpath(__file__)) 241c1ed15f1Sopenharmony_ci 242c1ed15f1Sopenharmony_ci user_policy_db = generate_database(input_args.cil_file) 243c1ed15f1Sopenharmony_ci developer_policy_db = generate_database(input_args.developer_cil_file) 244c1ed15f1Sopenharmony_ci check_rules = read_json_file(os.path.join(script_path, input_args.config)).get('check_rules') 245c1ed15f1Sopenharmony_ci result = False 246c1ed15f1Sopenharmony_ci for check_rule in check_rules: 247c1ed15f1Sopenharmony_ci result |= check_perm_group(input_args, check_rule, user_policy_db, False) 248c1ed15f1Sopenharmony_ci result |= check_perm_group(input_args, check_rule, developer_policy_db, True) 249c1ed15f1Sopenharmony_ci if result: 250c1ed15f1Sopenharmony_ci raise Exception(-1) 251