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 43HEADER_TEMPLATE = """/* DO NOT EDIT GENERATED FILE */ 44#ifndef MJ_SECCOMP_%(upper_name)s_H 45#define MJ_SECCOMP_%(upper_name)s_H 46#include <stdint.h> 47 48static const unsigned char %(name)s_binary_seccomp_policy[] __attribute__((__aligned__(4))) = { 49 %(program)s 50}; 51 52static const struct { 53 uint16_t cnt; 54 const void *bpf; 55} %(name)s_seccomp_bpf_program = { 56 .cnt = sizeof(%(name)s_binary_seccomp_policy) / 8, 57 .bpf = %(name)s_binary_seccomp_policy, 58}; 59 60#endif 61""" 62 63def parse_args(argv): 64 """Return the parsed CLI arguments for this tool.""" 65 arg_parser = argparse.ArgumentParser(description=__doc__) 66 arg_parser.add_argument('--optimization-strategy', 67 default=compiler.OptimizationStrategy.BST, 68 type=compiler.OptimizationStrategy, 69 choices=list(compiler.OptimizationStrategy)) 70 arg_parser.add_argument('--include-depth-limit', default=10) 71 arg_parser.add_argument('--arch-json', default='constants.json') 72 arg_parser.add_argument( 73 '--denylist', 74 action='store_true', 75 help='Compile as a denylist policy rather than the default allowlist.') 76 arg_parser.add_argument( 77 '--default-action', 78 type=str, 79 help=('Use the specified default action, overriding any @default ' 80 'action found in the .policy files. ' 81 'This allows the use of permissive actions (allow, log, trace, ' 82 'user-notify) since it is not valid to specify a permissive ' 83 'action in .policy files. This is useful for debugging.')) 84 arg_parser.add_argument( 85 '--use-kill-process', 86 action='store_true', 87 help=('Use SECCOMP_RET_KILL_PROCESS instead of ' 88 'SECCOMP_RET_KILL_THREAD (requires Linux v4.14+).')) 89 arg_parser.add_argument( 90 '--use-ret-log', 91 action='store_true', 92 help=('Change all seccomp failures to return SECCOMP_RET_LOG instead ' 93 'of killing (requires SECCOMP_RET_LOG kernel support).')) 94 arg_parser.add_argument( 95 '--output-header-file', 96 action='store_true', 97 help=('Output the compiled bpf to a constant variable in a C header ' 98 'file instead of a binary file (output should not have a .h ' 99 'extension, one will be added).')) 100 arg_parser.add_argument('policy', 101 help='The seccomp policy.', 102 type=argparse.FileType('r')) 103 arg_parser.add_argument('output', 104 help='The BPF program.') 105 return arg_parser.parse_args(argv), arg_parser 106 107 108def main(argv=None): 109 """Main entrypoint.""" 110 111 if argv is None: 112 argv = sys.argv[1:] 113 114 opts, arg_parser = parse_args(argv) 115 if not os.path.exists(opts.arch_json): 116 arg_parser.error(CONSTANTS_ERR_MSG) 117 118 parsed_arch = arch.Arch.load_from_json(opts.arch_json) 119 policy_compiler = compiler.PolicyCompiler(parsed_arch) 120 # Set ret_log to true if the MINIJAIL_DEFAULT_RET_LOG environment variable 121 # is present. 122 if 'MINIJAIL_DEFAULT_RET_LOG' in os.environ: 123 print(""" 124 \n********************** 125Warning: MINJAIL_DEFAULT_RET_LOG is on, policy will not have any effect 126**********************\n 127""") 128 opts.use_ret_log = True 129 if opts.use_ret_log: 130 kill_action = bpf.Log() 131 elif opts.denylist: 132 # Default action for a denylist policy is return EPERM 133 kill_action = bpf.ReturnErrno(parsed_arch.constants['EPERM']) 134 elif opts.use_kill_process: 135 kill_action = bpf.KillProcess() 136 else: 137 kill_action = bpf.KillThread() 138 override_default_action = None 139 if opts.default_action: 140 parser_state = parser.ParserState('<memory>') 141 override_default_action = parser.PolicyParser( 142 parsed_arch, kill_action=bpf.KillProcess()).parse_action( 143 next(parser_state.tokenize([opts.default_action]))) 144 145 compiled_policy = policy_compiler.compile_file( 146 opts.policy.name, 147 optimization_strategy=opts.optimization_strategy, 148 kill_action=kill_action, 149 include_depth_limit=opts.include_depth_limit, 150 override_default_action=override_default_action, 151 denylist=opts.denylist, 152 ret_log=opts.use_ret_log) 153 # Outputs the bpf binary to a c header file instead of a binary file. 154 if opts.output_header_file: 155 output_file_base = opts.output 156 with open(output_file_base + '.h', 'w') as output_file: 157 program = ', '.join('%i' % x for x in compiled_policy.opcodes) 158 output_file.write(HEADER_TEMPLATE % { 159 'upper_name': output_file_base.upper(), 160 'name': output_file_base, 161 'program': program, 162 }) 163 164 else: 165 with open(opts.output, 'wb') as outf: 166 outf.write(compiled_policy.opcodes) 167 return 0 168 169 170if __name__ == '__main__': 171 sys.exit(main(sys.argv[1:])) 172