1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2018 The Android Open Source Project
5#
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"""Helper tool to compile a BPF program from a Minijail seccomp filter.
18
19This script will take a Minijail seccomp policy file and compile it into a
20BPF program suitable for use with Minijail in the current architecture.
21"""
22
23from __future__ import print_function
24
25import argparse
26import os
27import sys
28
29try:
30    import arch
31    import bpf
32    import compiler
33    import parser
34except ImportError:
35    from minijail import arch
36    from minijail import bpf
37    from minijail import compiler
38    from minijail import parser
39
40CONSTANTS_ERR_MSG = """Could not find 'constants.json' file.
41See 'generate_constants_json.py -h'."""
42
43
44def parse_args(argv):
45    """Return the parsed CLI arguments for this tool."""
46    arg_parser = argparse.ArgumentParser(description=__doc__)
47    arg_parser.add_argument('--optimization-strategy',
48                            default=compiler.OptimizationStrategy.BST,
49                            type=compiler.OptimizationStrategy,
50                            choices=list(compiler.OptimizationStrategy))
51    arg_parser.add_argument('--include-depth-limit', default=10)
52    arg_parser.add_argument('--arch-json', default='constants.json')
53    arg_parser.add_argument(
54        '--default-action',
55        type=str,
56        help=('Use the specified default action, overriding any @default '
57              'action found in the .policy files. '
58              'This allows the use of permissive actions (allow, log, trace, '
59              'user-notify) since it is not valid to specify a permissive '
60              'action in .policy files. This is useful for debugging.'))
61    arg_parser.add_argument(
62        '--use-kill-process',
63        action='store_true',
64        help=('Use SECCOMP_RET_KILL_PROCESS instead of '
65              'SECCOMP_RET_KILL_THREAD (requires Linux v4.14+).'))
66    arg_parser.add_argument('policy',
67                            help='The seccomp policy.',
68                            type=argparse.FileType('r'))
69    arg_parser.add_argument('output',
70                            help='The BPF program.',
71                            type=argparse.FileType('wb'))
72    return arg_parser.parse_args(argv), arg_parser
73
74
75def main(argv=None):
76    """Main entrypoint."""
77
78    if argv is None:
79        argv = sys.argv[1:]
80
81    opts, arg_parser = parse_args(argv)
82    if not os.path.exists(opts.arch_json):
83        arg_parser.error(CONSTANTS_ERR_MSG)
84
85    parsed_arch = arch.Arch.load_from_json(opts.arch_json)
86    policy_compiler = compiler.PolicyCompiler(parsed_arch)
87    if opts.use_kill_process:
88        kill_action = bpf.KillProcess()
89    else:
90        kill_action = bpf.KillThread()
91    override_default_action = None
92    if opts.default_action:
93        parser_state = parser.ParserState('<memory>')
94        override_default_action = parser.PolicyParser(
95            parsed_arch, kill_action=bpf.KillProcess()).parse_action(
96                next(parser_state.tokenize([opts.default_action])))
97    with opts.output as outf:
98        outf.write(
99            policy_compiler.compile_file(
100                opts.policy.name,
101                optimization_strategy=opts.optimization_strategy,
102                kill_action=kill_action,
103                include_depth_limit=opts.include_depth_limit,
104                override_default_action=override_default_action).opcodes)
105    return 0
106
107
108if __name__ == '__main__':
109    sys.exit(main(sys.argv[1:]))
110