• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
25import sys
26import platform
27from collections import defaultdict
28sys.path.append(os.path.join(os.path.dirname(
29    os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))), "build"))
30from scripts.util import build_utils
31import find
32
33
34def parse_args():
35    parser = argparse.ArgumentParser()
36    parser.add_argument(
37        '--dst-dir', help='the output dest path', required=True)
38    parser.add_argument('--tool-path',
39                        help='the sefcontext_compile bin path', required=True)
40    parser.add_argument('--policy-file',
41                        help='the policy.31 file', required=True)
42    parser.add_argument('--source-root-dir',
43                        help='prj root path', required=True)
44    parser.add_argument('--policy_dir_list',
45                        help='policy dirs need to be included', required=True)
46    parser.add_argument('--components',
47                        help='system or vendor or default', required=True)
48    parser.add_argument('--depfile',
49                        help='depfile', required=True)
50    parser.add_argument('--output-file',
51                        help='output file', required=True)
52    parser.add_argument('--sepolicy-dir-lists',
53                        help='sepolicy dir lists', required=True)
54    return parser.parse_args()
55
56
57def run_command(in_cmd):
58    cmdstr = " ".join(in_cmd)
59    ret = subprocess.run(cmdstr, shell=True).returncode
60    if ret != 0:
61        raise Exception(ret)
62
63
64def traverse_folder_in_type(search_dir_list, file_suffix):
65    """
66    for special folder search_dir, find all files endwith file_suffix.
67    :param search_dir: path to search
68    :param file_suffix: postfix of file name
69    :return: file list
70    """
71    flag = 0
72    policy_file_list = []
73
74    for item in search_dir_list:
75        for root, _, files in sorted(os.walk(item)):
76            filtered_files = [f for f in files if f.endswith(file_suffix)]
77            for each_file in filtered_files:
78                file_list_path = os.path.join(root, each_file)
79                flag |= check_contexts_file(file_list_path)
80                policy_file_list.append(file_list_path)
81
82    if flag:
83        raise Exception(flag)
84    policy_file_list.sort()
85    return " ".join(str(x) for x in policy_file_list)
86
87
88def check_contexts_file(contexts_file):
89    """
90    Check the format of contexts file.
91    :param contexts_file: list of te file
92    :return:
93    """
94    err = 0
95    lines = []
96    with open(contexts_file, 'rb') as fp:
97        lines = fp.readlines()
98    if len(lines) == 0:
99        return 0
100    last_line = lines[-1]
101    if b'\n' not in last_line:
102        print("".join((contexts_file, " : need an empty line at end \n")))
103        err = 1
104    for line in lines:
105        if line.endswith(b'\r\n') or line.endswith(b'\r'):
106            print("".join((contexts_file, " : must be unix format\n")))
107            err = 1
108            break
109    return err
110
111
112def combine_contexts_file(file_contexts_list, combined_file_contexts):
113    cat_cmd = ["cat",
114               file_contexts_list,
115               ">", combined_file_contexts + "_tmp"]
116    run_command(cat_cmd)
117
118    grep_cmd = ["grep -v ^#",
119                combined_file_contexts + "_tmp",
120                "| grep -v ^$",
121                ">", combined_file_contexts]
122    run_command(grep_cmd)
123
124
125def check_redefinition(contexts_file):
126    type_hash = defaultdict(list)
127    err = 0
128    with open(contexts_file, 'r') as contexts_read:
129        pattern = re.compile(r'(\S+)\s+u:object_r:\S+:s0')
130        line_index = 0
131        for line in contexts_read:
132            line_ = line.lstrip()
133            line_index += 1
134            if line_.startswith('#') or line_.strip() == '':
135                continue
136            match = pattern.match(line_)
137            if match:
138                type_hash[match.group(1)].append(line_index)
139            else:
140                print(contexts_file + ":" +
141                      str(line_index) + " format check fail")
142                err = 1
143        contexts_read.close()
144    if err:
145        print("***********************************************************")
146        print("please check whether the format meets the following rules:")
147        print("[required format]: * u:object_r:*:s0")
148        print("***********************************************************")
149        raise Exception(err)
150    err = 0
151    for type_key in type_hash.keys():
152        if len(type_hash[type_key]) > 1:
153            err = 1
154            str_seq = (contexts_file, ":")
155            err_msg = "".join(str_seq)
156            for linenum in type_hash[type_key]:
157                str_seq = (err_msg, str(linenum), ":")
158                err_msg = "".join(str_seq)
159            str_seq = (err_msg, "'type ", str(type_key), " is redefinition'")
160            err_msg = "".join(str_seq)
161            print(err_msg)
162    if err:
163        raise Exception(err)
164
165
166def check_common_contexts(args, contexts_file):
167    """
168    check whether context used in contexts_file is defined in policy.31.
169    :param args:
170    :param contexts_file: path of contexts file
171    :return:
172    """
173    check_redefinition(contexts_file)
174
175    check_cmd = [os.path.join(args.tool_path, "sefcontext_compile"),
176                 "-o", contexts_file + ".bin",
177                 "-p", args.policy_file,
178                 contexts_file]
179    run_command(check_cmd)
180    if os.path.exists(contexts_file + ".bin"):
181        os.unlink(contexts_file + ".bin")
182
183
184def echo_error():
185    print("***********************************************************")
186    print("please check whether the format meets the following rules:")
187    print("[required format]: apl=* [name=*] [debuggable=*] [extension=*] [extra=*] domain=* type=*")
188    print("apl=*, apl should be one of system_core|system_basic|normal")
189    print("name=*, name is 'optional'")
190    print("debuggable=*, debuggable is 'optional'")
191    print("extension=*, extension is 'optional'")
192    print("extra=*, extra is 'optional'")
193    print("domain=*, hapdomain selinux type")
194    print("type=*, hapdatafile selinux type")
195    print("***********************************************************")
196
197
198def sehap_check_line(line, line_index, contexts_write, domain, contexts_file):
199    line_ = line.lstrip()
200    if line_.startswith('#') or line_.strip() == '':
201        contexts_write.write(line)
202        return
203
204    pattern = re.compile(
205        r'apl=(system_core|system_basic|normal)\s+'
206        r'(debuggable=\S+\s+)?'
207        r'(name=\S+\s+)?'
208        r'(extra=\S+\s+)?'
209        r'(extension=\S+\s+)?'
210        r'domain=(\S+)\s+'
211        r'type=(\S+)\s*'
212        r'.*\n',
213        re.DOTALL
214    )
215    match = pattern.match(line_)
216    if match:
217        if domain:
218            line = match.group(1) + " u:r:" + match.group(6) + ":s0\n"
219        else:
220            line = match.group(1) + " u:object_r:" + match.group(7) + ":s0\n"
221        contexts_write.write(line)
222    else:
223        print(contexts_file + ":" + str(line_index) + " format check fail")
224        raise Exception(1)
225
226
227def check_sehap_contexts(args, contexts_file, domain):
228    """
229    check domain or type defined in sehap_contexts.
230    :param args:
231    :param contexts_file: path of contexts file
232    :param domain: true for domain, false for type
233    :return:
234    """
235    shutil.copyfile(contexts_file, contexts_file + "_bk")
236    try:
237        with open(contexts_file + "_bk", 'r') as contexts_read, open(contexts_file, 'w') as contexts_write:
238            line_index = 0
239            for line in contexts_read:
240                line_index += 1
241                sehap_check_line(line, line_index, contexts_write, domain, contexts_file)
242    except Exception as e:
243        shutil.move(contexts_file + "_bk", contexts_file)
244        echo_error()
245        raise e
246
247    check_cmd = [os.path.join(args.tool_path, "sefcontext_compile"),
248                 "-o", contexts_file + ".bin",
249                 "-p", args.policy_file,
250                 contexts_file]
251    ret = subprocess.run(" ".join(check_cmd), shell=True).returncode
252
253    if ret != 0:
254        shutil.move(contexts_file + "_bk", contexts_file)
255        raise Exception(ret)
256
257    shutil.move(contexts_file + "_bk", contexts_file)
258
259    if os.path.exists(contexts_file + ".bin"):
260        os.unlink(contexts_file + ".bin")
261
262
263def build_file_contexts(args, output_path, policy_path, all_policy_path):
264    if args.components == "default":
265        all_combined_file_contexts = os.path.join(output_path, "file_contexts")
266    else:
267        all_combined_file_contexts = os.path.join(output_path, "all_file_contexts")
268        file_contexts_list = traverse_folder_in_type(policy_path, "file_contexts")
269        combined_file_contexts = os.path.join(output_path, "file_contexts")
270        combine_contexts_file(file_contexts_list, combined_file_contexts)
271
272    all_file_contexts_list = traverse_folder_in_type(
273        all_policy_path, "file_contexts")
274    combine_contexts_file(all_file_contexts_list, all_combined_file_contexts)
275
276    check_redefinition(all_combined_file_contexts)
277
278    build_bin_cmd = [os.path.join(args.tool_path, "sefcontext_compile"),
279                     "-o", os.path.join(args.dst_dir, "file_contexts.bin"),
280                     "-p", args.policy_file,
281                     all_combined_file_contexts]
282    run_command(build_bin_cmd)
283
284
285def build_common_contexts(args, output_path, contexts_file_name, policy_path, all_policy_path):
286    if args.components == "default":
287        all_combined_contexts = output_path + contexts_file_name
288    else:
289        all_combined_contexts = output_path + "all_" + contexts_file_name
290        contexts_list = traverse_folder_in_type(policy_path, contexts_file_name)
291        combined_contexts = output_path + contexts_file_name
292        combine_contexts_file(contexts_list, combined_contexts)
293
294    all_contexts_list = traverse_folder_in_type(all_policy_path, contexts_file_name)
295    combine_contexts_file(all_contexts_list, all_combined_contexts)
296    check_common_contexts(args, all_combined_contexts)
297
298
299def build_sehap_contexts(args, output_path, policy_path):
300    contexts_list = traverse_folder_in_type(
301        policy_path, "sehap_contexts")
302
303    combined_contexts = os.path.join(output_path, "sehap_contexts")
304    combine_contexts_file(contexts_list, combined_contexts)
305
306    check_sehap_contexts(args, combined_contexts, 1)
307    check_sehap_contexts(args, combined_contexts, 0)
308
309
310def prepare_build_path(dir_list, root_dir, build_dir_list):
311    build_contexts_list = \
312        ["base/security/selinux_adapter/sepolicy/base", "base/security/selinux_adapter/sepolicy/ohos_policy"]
313    build_contexts_list += dir_list.split(":")
314
315    for i in build_contexts_list:
316        if i == "" or i == "default":
317            continue
318        path = os.path.join(root_dir, i)
319        if (os.path.exists(path)):
320            build_dir_list.append(path)
321        else:
322            print("following path not exists!! {}".format(path))
323            exit(-1)
324
325
326def traverse_folder_in_dir_name(search_dir, folder_suffix):
327    folder_list = []
328    for root, dirs, _ in sorted(os.walk(search_dir)):
329        for dir_i in dirs:
330            if dir_i == folder_suffix:
331                folder_list.append(os.path.join(root, dir_i))
332    return folder_list
333
334
335def main(args):
336    if sys.platform == "linux" and platform.machine().lower() == "aarch64":
337        libpcre2_path = os.path.realpath("./clang_arm64/thirdparty/pcre2/")
338    else:
339        libpcre2_path = os.path.realpath("./clang_x64/thirdparty/pcre2/")
340    os.environ['LD_LIBRARY_PATH'] = libpcre2_path
341    output_path = args.dst_dir
342    policy_path = []
343    prepare_build_path(args.policy_dir_list, args.source_root_dir, policy_path)
344
345    public_policy = []
346    system_policy = []
347    vendor_policy = []
348
349    for item in policy_path:
350        public_policy += traverse_folder_in_dir_name(item, "public")
351        system_policy += traverse_folder_in_dir_name(item, "system")
352        vendor_policy += traverse_folder_in_dir_name(item, "vendor")
353
354    system_folder_list = public_policy + system_policy
355    vendor_folder_list = public_policy + vendor_policy
356    all_folder_list = public_policy + system_policy + vendor_policy
357
358    folder_list = []
359
360    if args.components == "system":
361        folder_list = system_folder_list
362    elif args.components == "vendor":
363        folder_list = vendor_folder_list
364    else:
365        folder_list = all_folder_list
366
367    build_file_contexts(args, output_path, folder_list, all_folder_list)
368    build_common_contexts(args, output_path, "service_contexts", folder_list, all_folder_list)
369    build_common_contexts(args, output_path, "hdf_service_contexts", folder_list, all_folder_list)
370    build_common_contexts(args, output_path, "parameter_contexts", folder_list, all_folder_list)
371    build_sehap_contexts(args, output_path, all_folder_list)
372
373
374if __name__ == "__main__":
375    input_args = parse_args()
376    if input_args.depfile:
377        dep_file = find.get_all_sepolicy_file(input_args.sepolicy_dir_lists)
378        dep_file.sort()
379        build_utils.write_depfile(input_args.depfile, input_args.output_file, dep_file, add_pydeps=False)
380    main(input_args)
381