1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4#
5# Copyright (c) 2023 Huawei Device Co., Ltd.
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import argparse
20import sys
21import os
22import audit_log_analysis as audit_policy
23import generate_code_from_policy as gen_policy
24
25
26
27def parse_line(fp):
28    func_name_set = set()
29    for line in fp:
30        line = line.strip()
31        func_name = line[:line.find('(')]
32        if ' ' in func_name:
33            func_name = func_name.split(' ')[-1].strip()
34        func_name_set.add(func_name)
35
36    return func_name_set
37
38
39def get_item_content(arch, func_name_set, name_nr_table):
40    func_name_to_nr = dict()
41    for func_name in func_name_set:
42        if func_name.startswith('arm_'):
43            func_name = func_name[len('arm_'):]
44        if func_name in name_nr_table.get(arch).keys():
45            func_name_to_nr.update({func_name: name_nr_table.get(arch).get(func_name)})
46
47    func_name_to_nr_list = sorted(func_name_to_nr.items(), key=lambda x : x[1])
48    content = '@allowList\n'
49    func_name_list = [func_name for func_name, _ in func_name_to_nr_list]
50
51    content = '{}{};{}\n'.format(content, ';{}\n'.format(arch).join(func_name_list), arch)
52
53    return content
54
55
56def parse_file(file_name):
57    with open(file_name) as f:
58        return parse_line(f)
59
60
61def parse_strace_log_to_policy(args):
62    file_list = extract_file_from_path(args.src_path)
63    function_name_nr_table_dict = {}
64    for file_name in file_list:
65        file_name_tmp = file_name.split('/')[-1]
66        if not file_name_tmp.lower().startswith('libsyscall_to_nr_'):
67            continue
68        gen_policy.gen_syscall_nr_table(file_name, function_name_nr_table_dict)
69
70    func_name_set = set()
71    for file_name in file_list:
72        if '.strace.log' in file_name.lower():
73            func_name_set |= parse_file(file_name)
74
75    content = get_item_content(args.target_cpu, func_name_set, function_name_nr_table_dict)
76    audit_policy.gen_output_file(args.filter_name, content)
77
78
79def extract_file_from_path(dir_path):
80    file_list = []
81    for path in dir_path:
82        if path[-1] == '/':
83            print('input dir path can not end with /')
84            return []
85
86        if os.path.isdir(path):
87            # get file list
88            file_list_tmp = os.listdir(path)
89            file_list += ['{}/{}'.format(path, item) for item in file_list_tmp]
90
91    return file_list
92
93
94def main():
95    parser = argparse.ArgumentParser(
96      description='Generates a seccomp-bpf policy')
97    parser.add_argument('--src-path', action='append',
98                        help='the path of directory which includes strace log and libsyscall_to_nr files')
99    parser.add_argument('--target-cpu', type=str,
100                        help='input arm or arm64 or riscv64')
101    parser.add_argument('--filter-name', type=str,
102                        help=('consist of output file name\n'))
103
104    args = parser.parse_args()
105    if args.target_cpu not in gen_policy.supported_architecture:
106        raise ValueError("target_cpu must int {}".format(gen_policy.supported_architecture))
107    parse_strace_log_to_policy(args)
108
109
110if __name__ == '__main__':
111    sys.exit(main())
112