1#!/usr/bin/env python 2# coding: utf-8 3 4""" 5Copyright (c) 2024 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 argparse 21import os 22from check_common import read_json_file, traverse_file_in_each_type 23 24WHITELIST_FILE_NAME = "ioctl_xperm_whitelist.json" 25 26ALLOW_TCONTEXT_CLASS_LIST = ["data_log_sanitizer_file file", "proc_attr dir", "self dir", "self fifo_file", 27 "self file", "self lnk_file", "self unix_stream_socket"] 28 29 30class PolicyDb(object): 31 def __init__(self, allowx_set, allow_set, typetransition_set): 32 self.allowx_set = allowx_set 33 self.allow_set = allow_set 34 self.typetransition_set = typetransition_set 35 36 37def simplify_string(string): 38 return string.strip().replace('(', '').replace(')', '') 39 40 41def split_allow_rule(elem_list, allow_set, allowx_set): 42 if len(elem_list) < 5: 43 print("not an allow/allowx rule: {}".format(elem_list)) 44 return 45 rulename = elem_list[0] 46 scontext = elem_list[1] 47 tcontext = elem_list[2] 48 tclass = elem_list[3] 49 if rulename == 'allow' and 'ioctl' in elem_list[4:]: 50 keycontent = f'{scontext} {tcontext} {tclass}' 51 allow_set.add(keycontent) 52 if rulename == 'allowx' and 'ioctl' == tclass: 53 keycontent = f'{scontext} {tcontext} {elem_list[4]}' 54 allowx_set.add(keycontent) 55 56 57def split_typetransition(elem_list, typetransition_set): 58 if len(elem_list) < 5: 59 print("not a typetransition rule: {}".format(elem_list)) 60 return 61 rulename = elem_list[0] 62 source_t = elem_list[1] 63 target_t = elem_list[2] 64 tclass = elem_list[3] 65 default_t = elem_list[4] 66 if tclass == 'process': 67 keycontent = f'{source_t} {target_t} file' 68 typetransition_set.add(keycontent) 69 70 71def deal_with_allow(cil_file, allow_set, allowx_set, typetransition_set): 72 with open(cil_file, 'r', encoding='utf-8') as cil_read: 73 for line in cil_read: 74 if line.startswith('(typetransition '): 75 # (typetransition A B process C) 76 sub_string = simplify_string(line) 77 elem_list = sub_string.split(' ') 78 split_typetransition(elem_list, typetransition_set) 79 80 if line.startswith('(allow ') or line.startswith('(allowx '): 81 sub_string = simplify_string(line) 82 elem_list = sub_string.split(' ') 83 # (allow A B (file (ioctl x x x))) 84 # (allowx A B (ioctl file (x x x))) 85 split_allow_rule(elem_list, allow_set, allowx_set) 86 87 88def generate_database(args, with_developer): 89 allowx_set = set() 90 allow_set = set() 91 typetransition_set = set() 92 if with_developer: 93 deal_with_allow(args.developer_cil_file, allow_set, allowx_set, typetransition_set) 94 else: 95 deal_with_allow(args.cil_file, allow_set, allowx_set, typetransition_set) 96 97 return PolicyDb(allowx_set, allow_set, typetransition_set) 98 99 100def get_whitelist(args, with_developer): 101 whitelist_file_list = traverse_file_in_each_type(args.policy_dir_list, WHITELIST_FILE_NAME) 102 contexts_list = [] 103 for path in whitelist_file_list: 104 white_list = read_json_file(path).get('whitelist') 105 contexts_list.extend(white_list.get('user')) 106 if with_developer: 107 contexts_list.extend(white_list.get('developer')) 108 return contexts_list 109 110 111def check(args, with_developer): 112 policy_db = generate_database(args, with_developer) 113 contexts_list = get_whitelist(args, with_developer) 114 diff_set = policy_db.allow_set - policy_db.allowx_set - policy_db.typetransition_set - set(contexts_list) 115 notallow = list() 116 for diff in diff_set: 117 if not (diff.endswith(tuple(ALLOW_TCONTEXT_CLASS_LIST))) : 118 notallow.append(diff) 119 120 if len(notallow) > 0 : 121 print('check ioctl rule in {} mode failed.'.format("developer" if with_developer else "user")) 122 print('violation list (allow scontext tcontext:tclass ioctl)') 123 for e in sorted(notallow): 124 elem_list = e.split(' ') 125 print('\tallow {} ioctl;'.format(elem_list[0] + ' ' + elem_list[1] + ':' + elem_list[2])) 126 print('please add "allowxperm" rule based on the above list.') 127 return len(notallow) > 0 128 129 130def parse_args(): 131 parser = argparse.ArgumentParser() 132 parser.add_argument('--cil_file', help='the cil file path', required=True) 133 parser.add_argument('--developer_cil_file', help='the developer cil file path', required=True) 134 parser.add_argument('--policy-dir-list', help='policy dirs need to be included', required=True) 135 136 return parser.parse_args() 137 138 139if __name__ == '__main__': 140 input_args = parse_args() 141 print("check xperm input_args: {}".format(input_args)) 142 result = check(input_args, False) 143 if result: 144 raise Exception(-1) 145 result = check(input_args, True) 146 if result: 147 raise Exception(-1) 148 149