• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# coding: utf-8
3
4"""
5Copyright (c) 2023 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 re
22import shutil
23import subprocess
24import tempfile
25
26SYSTEM_CIL_HASH = "system.cil.sha256"
27PREBUILD_SEPOLICY_SYSTEM_CIL_HASH = "prebuild_sepolicy.system.cil.sha256"
28
29# list of all macros and te for sepolicy build
30SEPOLICY_TYPE_LIST = ["security_classes",
31                      "initial_sids",
32                      "access_vectors",
33                      "glb_perm_def.spt",
34                      "glb_never_def.spt",
35                      "mls",
36                      "policy_cap",
37                      "glb_te_def.spt",
38                      "attributes",
39                      ".te",
40                      "glb_roles.spt",
41                      "users",
42                      "initial_sid_contexts",
43                      "fs_use",
44                      "virtfs_contexts",
45                      ]
46
47POLICY_TYPE_LIST = ["allow", "auditallow", "dontaudit",
48                    "allowx", "auditallowx", "dontauditx",
49                    "neverallow", "neverallowx", ]
50
51
52class PolicyDirList(object):
53    def __init__(self, min_policy_dir_list, system_policy_dir_list, vendor_policy_dir_list, public_policy_dir_list):
54        self.min_policy_dir_list = min_policy_dir_list
55        self.system_policy_dir_list = system_policy_dir_list
56        self.vendor_policy_dir_list = vendor_policy_dir_list
57        self.public_policy_dir_list = public_policy_dir_list
58
59
60class PolicyFileList(object):
61    def __init__(self, min_policy_file_list, system_policy_file_list, vendor_policy_file_list, public_policy_file_list):
62        self.min_policy_file_list = min_policy_file_list
63        self.system_policy_file_list = system_policy_file_list
64        self.vendor_policy_file_list = vendor_policy_file_list
65        self.public_policy_file_list = public_policy_file_list
66
67
68def traverse_folder_in_dir_name(search_dir, folder_suffix):
69    folder_list = []
70    for root, dirs, _ in os.walk(search_dir):
71        for dir_i in dirs:
72            if dir_i == folder_suffix:
73                folder_list.append(os.path.join(root, dir_i))
74    return folder_list
75
76
77def traverse_folder_in_type(search_dir, file_suffix, build_root):
78    policy_file_list = []
79    flag = 0
80    for root, _, files in os.walk(search_dir):
81        for each_file in files:
82            if each_file.endswith(file_suffix):
83                path = os.path.join(root, each_file)
84                rel_path = os.path.relpath(path, build_root)
85                flag |= check_empty_row(rel_path)
86                policy_file_list.append(rel_path)
87    policy_file_list.sort()
88    return policy_file_list, flag
89
90
91def traverse_file_in_each_type(folder_list, sepolicy_type_list, build_root):
92    policy_files_list = []
93    err = 0
94    for policy_type in sepolicy_type_list:
95        for folder in folder_list:
96            type_file_list, flag = traverse_folder_in_type(
97                folder, policy_type, build_root)
98            err |= flag
99            if len(type_file_list) == 0:
100                continue
101            policy_files_list.extend(type_file_list)
102    if err:
103        raise Exception(err)
104    return policy_files_list
105
106
107def check_empty_row(policy_file):
108    """
109    Check whether the last line of te file is empty.
110    :param policy_file: te file
111    :return:
112    """
113    err = 0
114    with open(policy_file, 'r') as fp:
115        lines = fp.readlines()
116        if len(lines) == 0:
117            return 0
118        last_line = lines[-1]
119        if '\n' not in last_line:
120            print("".join([policy_file, " : need an empty line at end\n"]))
121            err = 1
122    return err
123
124
125def run_command(in_cmd):
126    cmdstr = " ".join(in_cmd)
127    ret = subprocess.run(cmdstr, shell=True).returncode
128    if ret != 0:
129        raise Exception(ret)
130
131
132def build_conf(args, output_conf, file_list):
133    m4_args = ["-D", "build_with_debug=" + args.debug_version]
134    m4_args += ["-D", "build_with_updater=" + args.updater_version]
135    build_conf_cmd = ["m4", "-s", "--fatal-warnings"] + m4_args + file_list
136    with open(output_conf, 'w') as fd:
137        ret = subprocess.run(build_conf_cmd, shell=False, stdout=fd).returncode
138        if ret != 0:
139            raise Exception(ret)
140
141
142def build_cil(args, output_cil, input_conf):
143    check_policy_cmd = [os.path.join(args.tool_path, "checkpolicy"),
144                        input_conf,
145                        "-M -C -c 31",
146                        "-o " + output_cil]
147    run_command(check_policy_cmd)
148
149
150def add_version(version, string):
151    return "".join([string, "_", version])
152
153
154def simplify_string(string):
155    return string.replace('(', '').replace(')', '').replace('\n', '')
156
157
158def deal_with_roletype(version, cil_write, elem_list, type_set, file, line):
159    if len(elem_list) < 3:
160        print('Error: invalid roletype in %s:%d' % (file, line))
161        raise Exception(1)
162
163    sub_string = simplify_string(elem_list[2])
164    if sub_string in type_set:
165        cil_write.write('(typeattribute ' + add_version(version, sub_string) + ')\n')
166        elem_list[2] = elem_list[2].replace(
167            sub_string, add_version(version, sub_string))
168    cil_write.write(" ".join(elem_list))
169
170
171def deal_with_typeattribute(version, cil_write, elem_list, type_set, file, line):
172    if len(elem_list) < 2:
173        print('Error: invalid typeattribute in %s:%d' % (file, line))
174        raise Exception(1)
175
176    sub_string = simplify_string(elem_list[1])
177    if sub_string.startswith("base_typeattr_"):
178        elem_list[1] = elem_list[1].replace(
179            sub_string, add_version(version, sub_string))
180    cil_write.write(" ".join(elem_list))
181
182
183def deal_with_typeattributeset(version, cil_write, elem_list, type_set, file, line):
184    if len(elem_list) < 2:
185        print('Error: invalid typeattributeset in %s:%d' % (file, line))
186        raise Exception(1)
187
188    for index, elem in enumerate(elem_list[1:]):
189        sub_string = simplify_string(elem)
190        if sub_string.startswith("base_typeattr_") or sub_string in type_set:
191            elem_list[index + 1] = elem.replace(sub_string, add_version(version, sub_string))
192    cil_write.write(" ".join(elem_list))
193
194
195def deal_with_policy(version, cil_write, elem_list, type_set, file, line):
196    if len(elem_list) < 4:
197        print('Error: invalid policy in %s:%d' % (file, line))
198        raise Exception(1)
199
200    for index, elem in enumerate(elem_list[1:3]):
201        sub_string = simplify_string(elem)
202        if sub_string.startswith("base_typeattr_") or sub_string in type_set:
203            elem_list[index + 1] = elem.replace(sub_string, add_version(version, sub_string))
204    cil_write.write(" ".join(elem_list))
205
206
207def deal_with_type(version, cil_write, elem_list, file, line):
208    if len(elem_list) < 2:
209        print('Error: invalid type in %s:%d' % (file, line))
210        raise Exception(1)
211
212    sub_string = simplify_string(elem_list[1])
213    cil_write.write(" ".join(['(typeattributeset', add_version(version, sub_string), '(', sub_string, '))\n']))
214    cil_write.write(" ".join(['(expandtypeattribute', '(', add_version(version, sub_string), ') true)\n']))
215    cil_write.write(" ".join(['(typeattribute', add_version(version, sub_string), ')\n']))
216
217
218def build_version_cil(version, cil_file_input, cil_file_output, type_set):
219    index = 0
220    with open(cil_file_input, 'r') as cil_read, open(cil_file_output, 'w') as cil_write:
221        for line in cil_read:
222            index += 1
223            if not line.startswith('('):
224                continue
225
226            elem_list = line.split(' ')
227            if not elem_list:
228                continue
229
230            if elem_list[0] == '(type':
231                cil_write.write(line)
232            elif elem_list[0] == '(roletype':
233                deal_with_roletype(version, cil_write, elem_list, type_set, cil_file_input, line)
234            elif elem_list[0] == '(typeattribute':
235                deal_with_typeattribute(version, cil_write, elem_list, type_set, cil_file_input, line)
236            elif elem_list[0] == '(typeattributeset':
237                deal_with_typeattributeset(version, cil_write, elem_list, type_set, cil_file_input, line)
238            elif simplify_string(elem_list[0]) in POLICY_TYPE_LIST:
239                deal_with_policy(version, cil_write, elem_list, type_set, cil_file_input, line)
240            else:
241                cil_write.write(line)
242
243
244def build_type_version_cil(version, cil_file_input, cil_file_output):
245    index = 0
246    with open(cil_file_input, 'r') as cil_read, open(cil_file_output, 'w') as cil_write:
247        for line in cil_read:
248            index += 1
249            if not line.startswith('('):
250                continue
251
252            elem_list = line.split(' ')
253            if not elem_list:
254                continue
255
256            if elem_list[0] == '(type':
257                deal_with_type(version, cil_write, elem_list, line, index)
258
259
260def get_type_set(cil_file_input):
261    pattern_type = re.compile(r'^\(type (.*)\)$')
262    pattern_typeattribute = re.compile(r'^\(type_attribute (base_typeattr_[0-9]+)\)$')
263    type_set = set()
264    with open(cil_file_input, 'r') as cil_read:
265        for line in cil_read:
266            match_type = pattern_type.match(line)
267            match_typeattribute = pattern_typeattribute.match(line)
268            if match_type:
269                type_set.add(match_type.group(1))
270            elif match_typeattribute:
271                type_set.add(match_typeattribute.group(1))
272    return type_set
273
274
275def build_binary_policy(tool_path, output_policy, check_neverallow, cil_list):
276    build_policy_cmd = [os.path.join(tool_path, "secilc"),
277                        " ".join(cil_list),
278                        "-m -M true -G -c 31",
279                        "-f /dev/null",
280                        "-o " + output_policy]
281    if not check_neverallow:
282        build_policy_cmd.append("-N")
283    run_command(build_policy_cmd)
284
285
286def prepare_build_path(dir_list, root_dir, build_dir_list, sepolicy_path):
287    build_policy_list = [os.path.join(sepolicy_path, "base"), os.path.join(sepolicy_path, "ohos_policy")]
288    build_policy_list += dir_list.split(":")
289
290    for i in build_policy_list:
291        if i == "" or i == "default":
292            continue
293        path = os.path.join(root_dir, i)
294        if (os.path.exists(path)):
295            build_dir_list.append(path)
296        else:
297            print("following path not exists!! {}".format(path))
298            exit(-1)
299
300
301def get_policy_dir_list(args):
302    sepolicy_path = os.path.join(args.source_root_dir, "base/security/selinux_adapter/sepolicy/")
303    dir_list = []
304    prepare_build_path(args.policy_dir_list, args.source_root_dir, dir_list, sepolicy_path)
305    min_policy_dir_list = [os.path.join(sepolicy_path, "min")]
306    system_policy = []
307    public_policy = []
308    vendor_policy = []
309
310    for item in dir_list:
311        public_policy += traverse_folder_in_dir_name(item, "public")
312        system_policy += traverse_folder_in_dir_name(item, "system")
313        vendor_policy += traverse_folder_in_dir_name(item, "vendor")
314
315    # list of all policy folders
316    system_policy_dir_list = public_policy + system_policy
317    vendor_policy_dir_list = public_policy + vendor_policy + min_policy_dir_list
318    public_policy_dir_list = public_policy + min_policy_dir_list
319
320    # add temp dirs base/te folders
321    system_policy_dir_list.append(os.path.join(sepolicy_path, "base/te"))
322    vendor_policy_dir_list.append(os.path.join(sepolicy_path, "base/te"))
323    public_policy_dir_list.append(os.path.join(sepolicy_path, "base/te"))
324
325    return PolicyDirList(min_policy_dir_list, system_policy_dir_list, vendor_policy_dir_list, public_policy_dir_list)
326
327
328def get_policy_file_list(args, dir_list_object):
329    build_root = os.path.abspath(os.path.join(args.tool_path, "../../.."))
330    # list of all policy files
331    system_policy_file_list = traverse_file_in_each_type(
332        dir_list_object.system_policy_dir_list, SEPOLICY_TYPE_LIST, build_root)
333    vendor_policy_file_list = traverse_file_in_each_type(
334        dir_list_object.vendor_policy_dir_list, SEPOLICY_TYPE_LIST, build_root)
335    public_policy_file_list = traverse_file_in_each_type(
336        dir_list_object.public_policy_dir_list, SEPOLICY_TYPE_LIST, build_root)
337    min_policy_file_list = traverse_file_in_each_type(
338        dir_list_object.min_policy_dir_list, SEPOLICY_TYPE_LIST, build_root)
339
340    return PolicyFileList(min_policy_file_list, system_policy_file_list, vendor_policy_file_list,
341                          public_policy_file_list)
342
343
344def filter_out(pattern_file, input_file):
345    patterns = []
346    with open(pattern_file, 'r') as pat_file:
347        patterns.extend(pat_file.readlines())
348
349    tmp_output = tempfile.NamedTemporaryFile()
350    with open(input_file, 'r') as in_file:
351        tmp_output.writelines(line.encode(encoding='utf-8') for line in in_file.readlines()
352                              if line not in patterns)
353        tmp_output.write("\n".encode(encoding='utf-8'))
354        tmp_output.flush()
355    shutil.copyfile(tmp_output.name, input_file)
356
357
358def generate_hash_file(input_file_list, output_file):
359    build_policy_cmd = ["cat",
360                        " ".join(input_file_list),
361                        "| sha256sum",
362                        "| cut -d' ' -f1",
363                        ">",
364                        output_file]
365    run_command(build_policy_cmd)
366
367
368def generate_version_file(args, output_file):
369    cmd = ["echo", args.vendor_policy_version,
370           ">", output_file]
371    run_command(cmd)
372
373
374def generate_default_policy(args, system_policy_file_list, vendor_policy_file_list, min_policy_file_list):
375    output_path = os.path.abspath(os.path.dirname(args.dst_file))
376    system_output_conf = os.path.join(output_path, "system.conf")
377    vendor_output_conf = os.path.join(output_path, "vendor.conf")
378    min_output_conf = os.path.join(output_path, "min.conf")
379
380    system_cil_path = os.path.join(output_path, "system.cil")
381    vendor_cil_path = os.path.join(output_path, "vendor.cil")
382    min_cil_path = os.path.join(output_path, "min.cil")
383
384    # build system.conf
385    build_conf(args, system_output_conf, system_policy_file_list)
386    # build system.cil
387    build_cil(args, system_cil_path, system_output_conf)
388
389    # build vendor.conf
390    build_conf(args, vendor_output_conf, vendor_policy_file_list)
391    # build vendor.cil
392    build_cil(args, vendor_cil_path, vendor_output_conf)
393
394    # build min.conf
395    build_conf(args, min_output_conf, min_policy_file_list)
396    # build min.cil
397    build_cil(args, min_cil_path, min_output_conf)
398
399    filter_out(min_cil_path, vendor_cil_path)
400
401    return [vendor_cil_path, system_cil_path]
402
403
404def generate_special_policy(args, system_policy_file_list, vendor_policy_file_list, public_policy_file_list,
405                            min_policy_file_list):
406    output_path = os.path.abspath(os.path.dirname(args.dst_file))
407    system_output_conf = os.path.join(output_path, "system.conf")
408    vendor_output_conf = os.path.join(output_path, "vendor.conf")
409    public_output_conf = os.path.join(output_path, "public.conf")
410    min_output_conf = os.path.join(output_path, "min.conf")
411
412    vendor_origin_cil_path = os.path.join(output_path, "vendor_origin.cil")
413    public_origin_cil_path = os.path.join(output_path, "public_origin.cil")
414    min_cil_path = os.path.join(output_path, "min.cil")
415
416    # output file
417    system_cil_path = os.path.join(output_path, "system.cil")
418    vendor_cil_path = os.path.join(output_path, "vendor.cil")
419    public_version_cil_path = os.path.join(output_path, "public.cil")
420    type_version_cil_path = os.path.join(output_path, "".join([args.vendor_policy_version, ".cil"]))
421
422    # build system.conf
423    build_conf(args, system_output_conf, system_policy_file_list)
424    # build system.cil
425    build_cil(args, system_cil_path, system_output_conf)
426
427    # build min.cil
428    build_conf(args, min_output_conf, min_policy_file_list)
429    build_cil(args, min_cil_path, min_output_conf)
430
431    # build public.cil
432    build_conf(args, public_output_conf, public_policy_file_list)
433    build_cil(args, public_origin_cil_path, public_output_conf)
434    type_set = get_type_set(public_origin_cil_path)
435    filter_out(min_cil_path, public_origin_cil_path)
436    build_version_cil(args.vendor_policy_version, public_origin_cil_path, public_version_cil_path, type_set)
437
438    # build vendor.cil
439    build_conf(args, vendor_output_conf, vendor_policy_file_list)
440    build_cil(args, vendor_origin_cil_path, vendor_output_conf)
441    filter_out(min_cil_path, vendor_origin_cil_path)
442    build_version_cil(args.vendor_policy_version, vendor_origin_cil_path, vendor_cil_path, type_set)
443    filter_out(public_version_cil_path, vendor_cil_path)
444
445    build_type_version_cil(args.vendor_policy_version, public_origin_cil_path, type_version_cil_path)
446
447    if args.components == "system":
448        generate_hash_file([system_cil_path, type_version_cil_path],
449                           os.path.join(output_path, SYSTEM_CIL_HASH))
450
451    elif args.components == "vendor":
452        generate_hash_file([system_cil_path, type_version_cil_path], os.path.join(
453            output_path, PREBUILD_SEPOLICY_SYSTEM_CIL_HASH))
454
455    version_file = os.path.join(output_path, "version")
456    generate_version_file(args, version_file)
457
458    return [vendor_cil_path, system_cil_path, type_version_cil_path, public_version_cil_path]
459
460
461def compile_sepolicy(args):
462    dir_list_object = get_policy_dir_list(args)
463    file_list_object = get_policy_file_list(args, dir_list_object)
464
465    cil_list = []
466    if args.components == "default" or args.updater_version == "enable":
467        cil_list += generate_default_policy(args, file_list_object.system_policy_file_list,
468                                            file_list_object.vendor_policy_file_list,
469                                            file_list_object.min_policy_file_list)
470    else:
471        cil_list += generate_special_policy(args, file_list_object.system_policy_file_list,
472                                            file_list_object.vendor_policy_file_list,
473                                            file_list_object.public_policy_file_list,
474                                            file_list_object.min_policy_file_list)
475
476    build_binary_policy(args.tool_path, args.dst_file, True, cil_list)
477
478
479def main(args):
480    # check both debug and release sepolicy
481    origin_debug_version = args.debug_version
482    if args.debug_version == "true":
483        args.debug_version = "false"
484        compile_sepolicy(args)
485    else:
486        args.debug_version = "true"
487        compile_sepolicy(args)
488
489    # build target policy according to desire debug_version
490    args.debug_version = origin_debug_version
491    compile_sepolicy(args)
492