172 lines
6.1 KiB
Python
Executable File
172 lines
6.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2018 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Helper tool to compile a BPF program from a Minijail seccomp filter.
|
|
|
|
This script will take a Minijail seccomp policy file and compile it into a
|
|
BPF program suitable for use with Minijail in the current architecture.
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
try:
|
|
import arch
|
|
import bpf
|
|
import compiler
|
|
import parser
|
|
except ImportError:
|
|
from minijail import arch
|
|
from minijail import bpf
|
|
from minijail import compiler
|
|
from minijail import parser
|
|
|
|
CONSTANTS_ERR_MSG = """Could not find 'constants.json' file.
|
|
See 'generate_constants_json.py -h'."""
|
|
|
|
HEADER_TEMPLATE = """/* DO NOT EDIT GENERATED FILE */
|
|
#ifndef MJ_SECCOMP_%(upper_name)s_H
|
|
#define MJ_SECCOMP_%(upper_name)s_H
|
|
#include <stdint.h>
|
|
|
|
static const unsigned char %(name)s_binary_seccomp_policy[] __attribute__((__aligned__(4))) = {
|
|
%(program)s
|
|
};
|
|
|
|
static const struct {
|
|
uint16_t cnt;
|
|
const void *bpf;
|
|
} %(name)s_seccomp_bpf_program = {
|
|
.cnt = sizeof(%(name)s_binary_seccomp_policy) / 8,
|
|
.bpf = %(name)s_binary_seccomp_policy,
|
|
};
|
|
|
|
#endif
|
|
"""
|
|
|
|
def parse_args(argv):
|
|
"""Return the parsed CLI arguments for this tool."""
|
|
arg_parser = argparse.ArgumentParser(description=__doc__)
|
|
arg_parser.add_argument('--optimization-strategy',
|
|
default=compiler.OptimizationStrategy.BST,
|
|
type=compiler.OptimizationStrategy,
|
|
choices=list(compiler.OptimizationStrategy))
|
|
arg_parser.add_argument('--include-depth-limit', default=10)
|
|
arg_parser.add_argument('--arch-json', default='constants.json')
|
|
arg_parser.add_argument(
|
|
'--denylist',
|
|
action='store_true',
|
|
help='Compile as a denylist policy rather than the default allowlist.')
|
|
arg_parser.add_argument(
|
|
'--default-action',
|
|
type=str,
|
|
help=('Use the specified default action, overriding any @default '
|
|
'action found in the .policy files. '
|
|
'This allows the use of permissive actions (allow, log, trace, '
|
|
'user-notify) since it is not valid to specify a permissive '
|
|
'action in .policy files. This is useful for debugging.'))
|
|
arg_parser.add_argument(
|
|
'--use-kill-process',
|
|
action='store_true',
|
|
help=('Use SECCOMP_RET_KILL_PROCESS instead of '
|
|
'SECCOMP_RET_KILL_THREAD (requires Linux v4.14+).'))
|
|
arg_parser.add_argument(
|
|
'--use-ret-log',
|
|
action='store_true',
|
|
help=('Change all seccomp failures to return SECCOMP_RET_LOG instead '
|
|
'of killing (requires SECCOMP_RET_LOG kernel support).'))
|
|
arg_parser.add_argument(
|
|
'--output-header-file',
|
|
action='store_true',
|
|
help=('Output the compiled bpf to a constant variable in a C header '
|
|
'file instead of a binary file (output should not have a .h '
|
|
'extension, one will be added).'))
|
|
arg_parser.add_argument('policy',
|
|
help='The seccomp policy.',
|
|
type=argparse.FileType('r'))
|
|
arg_parser.add_argument('output',
|
|
help='The BPF program.')
|
|
return arg_parser.parse_args(argv), arg_parser
|
|
|
|
|
|
def main(argv=None):
|
|
"""Main entrypoint."""
|
|
|
|
if argv is None:
|
|
argv = sys.argv[1:]
|
|
|
|
opts, arg_parser = parse_args(argv)
|
|
if not os.path.exists(opts.arch_json):
|
|
arg_parser.error(CONSTANTS_ERR_MSG)
|
|
|
|
parsed_arch = arch.Arch.load_from_json(opts.arch_json)
|
|
policy_compiler = compiler.PolicyCompiler(parsed_arch)
|
|
# Set ret_log to true if the MINIJAIL_DEFAULT_RET_LOG environment variable
|
|
# is present.
|
|
if 'MINIJAIL_DEFAULT_RET_LOG' in os.environ:
|
|
print("""
|
|
\n**********************
|
|
Warning: MINJAIL_DEFAULT_RET_LOG is on, policy will not have any effect
|
|
**********************\n
|
|
""")
|
|
opts.use_ret_log = True
|
|
if opts.use_ret_log:
|
|
kill_action = bpf.Log()
|
|
elif opts.denylist:
|
|
# Default action for a denylist policy is return EPERM
|
|
kill_action = bpf.ReturnErrno(parsed_arch.constants['EPERM'])
|
|
elif opts.use_kill_process:
|
|
kill_action = bpf.KillProcess()
|
|
else:
|
|
kill_action = bpf.KillThread()
|
|
override_default_action = None
|
|
if opts.default_action:
|
|
parser_state = parser.ParserState('<memory>')
|
|
override_default_action = parser.PolicyParser(
|
|
parsed_arch, kill_action=bpf.KillProcess()).parse_action(
|
|
next(parser_state.tokenize([opts.default_action])))
|
|
|
|
compiled_policy = policy_compiler.compile_file(
|
|
opts.policy.name,
|
|
optimization_strategy=opts.optimization_strategy,
|
|
kill_action=kill_action,
|
|
include_depth_limit=opts.include_depth_limit,
|
|
override_default_action=override_default_action,
|
|
denylist=opts.denylist,
|
|
ret_log=opts.use_ret_log)
|
|
# Outputs the bpf binary to a c header file instead of a binary file.
|
|
if opts.output_header_file:
|
|
output_file_base = opts.output
|
|
with open(output_file_base + '.h', 'w') as output_file:
|
|
program = ', '.join('%i' % x for x in compiled_policy.opcodes)
|
|
output_file.write(HEADER_TEMPLATE % {
|
|
'upper_name': output_file_base.upper(),
|
|
'name': output_file_base,
|
|
'program': program,
|
|
})
|
|
|
|
else:
|
|
with open(opts.output, 'wb') as outf:
|
|
outf.write(compiled_policy.opcodes)
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(sys.argv[1:]))
|