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