1*4b9c6d91SCole Faust#!/usr/bin/env python3 2*4b9c6d91SCole Faust# -*- coding: utf-8 -*- 3*4b9c6d91SCole Faust# 4*4b9c6d91SCole Faust# Copyright (C) 2016 The Android Open Source Project 5*4b9c6d91SCole Faust# 6*4b9c6d91SCole Faust# Licensed under the Apache License, Version 2.0 (the "License"); 7*4b9c6d91SCole Faust# you may not use this file except in compliance with the License. 8*4b9c6d91SCole Faust# You may obtain a copy of the License at 9*4b9c6d91SCole Faust# 10*4b9c6d91SCole Faust# http://www.apache.org/licenses/LICENSE-2.0 11*4b9c6d91SCole Faust# 12*4b9c6d91SCole Faust# Unless required by applicable law or agreed to in writing, software 13*4b9c6d91SCole Faust# distributed under the License is distributed on an "AS IS" BASIS, 14*4b9c6d91SCole Faust# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15*4b9c6d91SCole Faust# See the License for the specific language governing permissions and 16*4b9c6d91SCole Faust# limitations under the License. 17*4b9c6d91SCole Faust# 18*4b9c6d91SCole Faust# This script will take any number of trace files generated by strace(1) 19*4b9c6d91SCole Faust# and output a system call filtering policy suitable for use with Minijail. 20*4b9c6d91SCole Faust 21*4b9c6d91SCole Faust"""Tool to generate a minijail seccomp filter from strace or audit output.""" 22*4b9c6d91SCole Faust 23*4b9c6d91SCole Faustfrom __future__ import print_function 24*4b9c6d91SCole Faust 25*4b9c6d91SCole Faustimport argparse 26*4b9c6d91SCole Faustimport collections 27*4b9c6d91SCole Faustimport os 28*4b9c6d91SCole Faustimport re 29*4b9c6d91SCole Faustimport sys 30*4b9c6d91SCole Faust 31*4b9c6d91SCole Faust# auparse may not be installed and is currently optional. 32*4b9c6d91SCole Fausttry: 33*4b9c6d91SCole Faust import auparse 34*4b9c6d91SCole Faustexcept ImportError: 35*4b9c6d91SCole Faust auparse = None 36*4b9c6d91SCole Faust 37*4b9c6d91SCole Faust 38*4b9c6d91SCole FaustNOTICE = """# Copyright (C) 2018 The Android Open Source Project 39*4b9c6d91SCole Faust# 40*4b9c6d91SCole Faust# Licensed under the Apache License, Version 2.0 (the "License"); 41*4b9c6d91SCole Faust# you may not use this file except in compliance with the License. 42*4b9c6d91SCole Faust# You may obtain a copy of the License at 43*4b9c6d91SCole Faust# 44*4b9c6d91SCole Faust# http://www.apache.org/licenses/LICENSE-2.0 45*4b9c6d91SCole Faust# 46*4b9c6d91SCole Faust# Unless required by applicable law or agreed to in writing, software 47*4b9c6d91SCole Faust# distributed under the License is distributed on an "AS IS" BASIS, 48*4b9c6d91SCole Faust# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49*4b9c6d91SCole Faust# See the License for the specific language governing permissions and 50*4b9c6d91SCole Faust# limitations under the License. 51*4b9c6d91SCole Faust""" 52*4b9c6d91SCole Faust 53*4b9c6d91SCole FaustALLOW = '1' 54*4b9c6d91SCole Faust 55*4b9c6d91SCole Faust# This ignores any leading PID tag and trailing <unfinished ...>, and extracts 56*4b9c6d91SCole Faust# the syscall name and the argument list. 57*4b9c6d91SCole FaustLINE_RE = re.compile(r'^\s*(?:\[[^]]*\]|\d+)?\s*([a-zA-Z0-9_]+)\(([^)<]*)') 58*4b9c6d91SCole Faust 59*4b9c6d91SCole FaustSOCKETCALLS = { 60*4b9c6d91SCole Faust 'accept', 'bind', 'connect', 'getpeername', 'getsockname', 'getsockopt', 61*4b9c6d91SCole Faust 'listen', 'recv', 'recvfrom', 'recvmsg', 'send', 'sendmsg', 'sendto', 62*4b9c6d91SCole Faust 'setsockopt', 'shutdown', 'socket', 'socketpair', 63*4b9c6d91SCole Faust} 64*4b9c6d91SCole Faust 65*4b9c6d91SCole Faust# List of private ARM syscalls. These can be found in any ARM specific unistd.h 66*4b9c6d91SCole Faust# such as Linux's arch/arm/include/uapi/asm/unistd.h. 67*4b9c6d91SCole FaustPRIVATE_ARM_SYSCALLS = { 68*4b9c6d91SCole Faust 983041: 'ARM_breakpoint', 69*4b9c6d91SCole Faust 983042: 'ARM_cacheflush', 70*4b9c6d91SCole Faust 983043: 'ARM_usr26', 71*4b9c6d91SCole Faust 983044: 'ARM_usr32', 72*4b9c6d91SCole Faust 983045: 'ARM_set_tls', 73*4b9c6d91SCole Faust} 74*4b9c6d91SCole Faust 75*4b9c6d91SCole FaustArgInspectionEntry = collections.namedtuple('ArgInspectionEntry', 76*4b9c6d91SCole Faust ('arg_index', 'value_set')) 77*4b9c6d91SCole Faust 78*4b9c6d91SCole Faust 79*4b9c6d91SCole Faust# pylint: disable=too-few-public-methods 80*4b9c6d91SCole Faustclass BucketInputFiles(argparse.Action): 81*4b9c6d91SCole Faust """Buckets input files using simple content based heuristics. 82*4b9c6d91SCole Faust 83*4b9c6d91SCole Faust Attributes: 84*4b9c6d91SCole Faust audit_logs: Mutually exclusive list of audit log filenames. 85*4b9c6d91SCole Faust traces: Mutually exclusive list of strace log filenames. 86*4b9c6d91SCole Faust """ 87*4b9c6d91SCole Faust def __call__(self, parser, namespace, values, option_string=None): 88*4b9c6d91SCole Faust audit_logs = [] 89*4b9c6d91SCole Faust traces = [] 90*4b9c6d91SCole Faust 91*4b9c6d91SCole Faust strace_line_re = re.compile(r'[a-z]+[0-9]*\(.+\) += ') 92*4b9c6d91SCole Faust audit_line_re = re.compile(r'type=(SYSCALL|SECCOMP)') 93*4b9c6d91SCole Faust 94*4b9c6d91SCole Faust for filename in values: 95*4b9c6d91SCole Faust if not os.path.exists(filename): 96*4b9c6d91SCole Faust parser.error(f'Input file {filename} not found.') 97*4b9c6d91SCole Faust with open(filename, mode='r', encoding='utf8') as input_file: 98*4b9c6d91SCole Faust for line in input_file.readlines(): 99*4b9c6d91SCole Faust if strace_line_re.search(line): 100*4b9c6d91SCole Faust traces.append(filename) 101*4b9c6d91SCole Faust break 102*4b9c6d91SCole Faust if audit_line_re.search(line): 103*4b9c6d91SCole Faust audit_logs.append(filename) 104*4b9c6d91SCole Faust break 105*4b9c6d91SCole Faust else: 106*4b9c6d91SCole Faust # Treat it as an strace log to retain legacy behaviour and 107*4b9c6d91SCole Faust # also just in case the strace regex is imperfect. 108*4b9c6d91SCole Faust traces.append(filename) 109*4b9c6d91SCole Faust 110*4b9c6d91SCole Faust setattr(namespace, 'audit_logs', audit_logs) 111*4b9c6d91SCole Faust setattr(namespace, 'traces', traces) 112*4b9c6d91SCole Faust# pylint: enable=too-few-public-methods 113*4b9c6d91SCole Faust 114*4b9c6d91SCole Faust 115*4b9c6d91SCole Faustdef parse_args(argv): 116*4b9c6d91SCole Faust """Returns the parsed CLI arguments for this tool.""" 117*4b9c6d91SCole Faust parser = argparse.ArgumentParser(description=__doc__) 118*4b9c6d91SCole Faust parser.add_argument('--verbose', action='store_true', 119*4b9c6d91SCole Faust help='output informational messages to stderr') 120*4b9c6d91SCole Faust parser.add_argument('--frequency', type=argparse.FileType('w'), 121*4b9c6d91SCole Faust help='frequency file') 122*4b9c6d91SCole Faust parser.add_argument('--policy', type=argparse.FileType('w'), 123*4b9c6d91SCole Faust default=sys.stdout, help='policy file') 124*4b9c6d91SCole Faust parser.add_argument('input-logs', action=BucketInputFiles, 125*4b9c6d91SCole Faust help='strace and/or audit logs', nargs='+') 126*4b9c6d91SCole Faust parser.add_argument('--audit-comm', type=str, metavar='PROCESS_NAME', 127*4b9c6d91SCole Faust help='relevant process name from the audit.log files') 128*4b9c6d91SCole Faust opts = parser.parse_args(argv) 129*4b9c6d91SCole Faust 130*4b9c6d91SCole Faust if opts.audit_logs and not auparse: 131*4b9c6d91SCole Faust parser.error('Python bindings for the audit subsystem were not found.\n' 132*4b9c6d91SCole Faust 'Please install the python3-audit (sometimes python-audit)' 133*4b9c6d91SCole Faust ' package for your distro to process audit logs: ' 134*4b9c6d91SCole Faust f'{opts.audit_logs}') 135*4b9c6d91SCole Faust 136*4b9c6d91SCole Faust if opts.audit_logs and not opts.audit_comm: 137*4b9c6d91SCole Faust parser.error(f'--audit-comm is required when using audit logs as input:' 138*4b9c6d91SCole Faust f' {opts.audit_logs}') 139*4b9c6d91SCole Faust 140*4b9c6d91SCole Faust if not opts.audit_logs and opts.audit_comm: 141*4b9c6d91SCole Faust parser.error('--audit-comm was specified yet none of the input files ' 142*4b9c6d91SCole Faust 'matched our hueristic for an audit log') 143*4b9c6d91SCole Faust 144*4b9c6d91SCole Faust return opts 145*4b9c6d91SCole Faust 146*4b9c6d91SCole Faust 147*4b9c6d91SCole Faustdef get_seccomp_bpf_filter(syscall, entry): 148*4b9c6d91SCole Faust """Returns a minijail seccomp-bpf filter expression for the syscall.""" 149*4b9c6d91SCole Faust arg_index = entry.arg_index 150*4b9c6d91SCole Faust arg_values = entry.value_set 151*4b9c6d91SCole Faust atoms = [] 152*4b9c6d91SCole Faust if syscall in ('mmap', 'mmap2', 'mprotect') and arg_index == 2: 153*4b9c6d91SCole Faust # See if there is at least one instance of any of these syscalls trying 154*4b9c6d91SCole Faust # to map memory with both PROT_EXEC and PROT_WRITE. If there isn't, we 155*4b9c6d91SCole Faust # can craft a concise expression to forbid this. 156*4b9c6d91SCole Faust write_and_exec = set(('PROT_EXEC', 'PROT_WRITE')) 157*4b9c6d91SCole Faust for arg_value in arg_values: 158*4b9c6d91SCole Faust if write_and_exec.issubset(set(p.strip() for p in 159*4b9c6d91SCole Faust arg_value.split('|'))): 160*4b9c6d91SCole Faust break 161*4b9c6d91SCole Faust else: 162*4b9c6d91SCole Faust atoms.extend(['arg2 in ~PROT_EXEC', 'arg2 in ~PROT_WRITE']) 163*4b9c6d91SCole Faust arg_values = set() 164*4b9c6d91SCole Faust atoms.extend(f'arg{arg_index} == {arg_value}' for arg_value in arg_values) 165*4b9c6d91SCole Faust return ' || '.join(atoms) 166*4b9c6d91SCole Faust 167*4b9c6d91SCole Faust 168*4b9c6d91SCole Faustdef parse_trace_file(trace_filename, syscalls, arg_inspection): 169*4b9c6d91SCole Faust """Parses one file produced by strace.""" 170*4b9c6d91SCole Faust uses_socketcall = ('i386' in trace_filename or 171*4b9c6d91SCole Faust ('x86' in trace_filename and 172*4b9c6d91SCole Faust '64' not in trace_filename)) 173*4b9c6d91SCole Faust 174*4b9c6d91SCole Faust with open(trace_filename, encoding='utf8') as trace_file: 175*4b9c6d91SCole Faust for line in trace_file: 176*4b9c6d91SCole Faust matches = LINE_RE.match(line) 177*4b9c6d91SCole Faust if not matches: 178*4b9c6d91SCole Faust continue 179*4b9c6d91SCole Faust 180*4b9c6d91SCole Faust syscall, args = matches.groups() 181*4b9c6d91SCole Faust if uses_socketcall and syscall in SOCKETCALLS: 182*4b9c6d91SCole Faust syscall = 'socketcall' 183*4b9c6d91SCole Faust 184*4b9c6d91SCole Faust # strace omits the 'ARM_' prefix on all private ARM syscalls. Add 185*4b9c6d91SCole Faust # it manually here as a workaround. These syscalls are exclusive 186*4b9c6d91SCole Faust # to ARM so we don't need to predicate this on a trace_filename 187*4b9c6d91SCole Faust # based heuristic for the arch. 188*4b9c6d91SCole Faust if f'ARM_{syscall}' in PRIVATE_ARM_SYSCALLS.values(): 189*4b9c6d91SCole Faust syscall = f'ARM_{syscall}' 190*4b9c6d91SCole Faust 191*4b9c6d91SCole Faust syscalls[syscall] += 1 192*4b9c6d91SCole Faust 193*4b9c6d91SCole Faust args = [arg.strip() for arg in args.split(',')] 194*4b9c6d91SCole Faust 195*4b9c6d91SCole Faust if syscall in arg_inspection: 196*4b9c6d91SCole Faust arg_value = args[arg_inspection[syscall].arg_index] 197*4b9c6d91SCole Faust arg_inspection[syscall].value_set.add(arg_value) 198*4b9c6d91SCole Faust 199*4b9c6d91SCole Faust 200*4b9c6d91SCole Faustdef parse_audit_log(audit_log, audit_comm, syscalls, arg_inspection): 201*4b9c6d91SCole Faust """Parses one audit.log file generated by the Linux audit subsystem.""" 202*4b9c6d91SCole Faust 203*4b9c6d91SCole Faust unknown_syscall_re = re.compile(r'unknown-syscall\((?P<syscall_num>\d+)\)') 204*4b9c6d91SCole Faust 205*4b9c6d91SCole Faust au = auparse.AuParser(auparse.AUSOURCE_FILE, audit_log) 206*4b9c6d91SCole Faust # Quick validity check for whether this parses as a valid audit log. The 207*4b9c6d91SCole Faust # first event should have at least one record. 208*4b9c6d91SCole Faust if not au.first_record(): 209*4b9c6d91SCole Faust raise ValueError(f'Unable to parse audit log file {audit_log.name}') 210*4b9c6d91SCole Faust 211*4b9c6d91SCole Faust # Iterate through events where _any_ contained record matches 212*4b9c6d91SCole Faust # ((type == SECCOMP || type == SYSCALL) && comm == audit_comm). 213*4b9c6d91SCole Faust au.search_add_item('type', '=', 'SECCOMP', auparse.AUSEARCH_RULE_CLEAR) 214*4b9c6d91SCole Faust au.search_add_item('type', '=', 'SYSCALL', auparse.AUSEARCH_RULE_OR) 215*4b9c6d91SCole Faust au.search_add_item('comm', '=', f'"{audit_comm}"', 216*4b9c6d91SCole Faust auparse.AUSEARCH_RULE_AND) 217*4b9c6d91SCole Faust 218*4b9c6d91SCole Faust # auparse_find_field(3) will ignore preceding fields in the record and 219*4b9c6d91SCole Faust # at the same time happily cross record boundaries when looking for the 220*4b9c6d91SCole Faust # field. This helper method always seeks the cursor back to the first 221*4b9c6d91SCole Faust # field in the record and stops searching before crossing over to the 222*4b9c6d91SCole Faust # next record; making the search far less error prone. 223*4b9c6d91SCole Faust # Also implicitly seeks the internal 'cursor' to the matching field 224*4b9c6d91SCole Faust # for any subsequent calls like auparse_interpret_field. 225*4b9c6d91SCole Faust def _find_field_in_current_record(name): 226*4b9c6d91SCole Faust au.first_field() 227*4b9c6d91SCole Faust while True: 228*4b9c6d91SCole Faust if au.get_field_name() == name: 229*4b9c6d91SCole Faust return au.get_field_str() 230*4b9c6d91SCole Faust if not au.next_field(): 231*4b9c6d91SCole Faust return None 232*4b9c6d91SCole Faust 233*4b9c6d91SCole Faust while au.search_next_event(): 234*4b9c6d91SCole Faust # The event may have multiple records. Loop through all. 235*4b9c6d91SCole Faust au.first_record() 236*4b9c6d91SCole Faust for _ in range(au.get_num_records()): 237*4b9c6d91SCole Faust event_type = _find_field_in_current_record('type') 238*4b9c6d91SCole Faust comm = _find_field_in_current_record('comm') 239*4b9c6d91SCole Faust # Some of the records in this event may not be relevant 240*4b9c6d91SCole Faust # despite the event-specific search filter. Skip those. 241*4b9c6d91SCole Faust if (event_type not in ('SECCOMP', 'SYSCALL') or 242*4b9c6d91SCole Faust comm != f'"{audit_comm}"'): 243*4b9c6d91SCole Faust au.next_record() 244*4b9c6d91SCole Faust continue 245*4b9c6d91SCole Faust 246*4b9c6d91SCole Faust if not _find_field_in_current_record('syscall'): 247*4b9c6d91SCole Faust raise ValueError(f'Could not find field "syscall" in event of ' 248*4b9c6d91SCole Faust f'type {event_type}') 249*4b9c6d91SCole Faust # Intepret the syscall field that's under our 'cursor' following the 250*4b9c6d91SCole Faust # find. Interpreting fields yields human friendly names instead 251*4b9c6d91SCole Faust # of integers. E.g '16' -> 'ioctl'. 252*4b9c6d91SCole Faust syscall = au.interpret_field() 253*4b9c6d91SCole Faust 254*4b9c6d91SCole Faust # TODO(crbug/1172449): Add these syscalls to upstream 255*4b9c6d91SCole Faust # audit-userspace and remove this workaround. 256*4b9c6d91SCole Faust # This is redundant but safe for non-ARM architectures due to the 257*4b9c6d91SCole Faust # disjoint set of private syscall numbers. 258*4b9c6d91SCole Faust match = unknown_syscall_re.match(syscall) 259*4b9c6d91SCole Faust if match: 260*4b9c6d91SCole Faust syscall_num = int(match.group('syscall_num')) 261*4b9c6d91SCole Faust syscall = PRIVATE_ARM_SYSCALLS.get(syscall_num, syscall) 262*4b9c6d91SCole Faust 263*4b9c6d91SCole Faust if ((syscall in arg_inspection and event_type == 'SECCOMP') or 264*4b9c6d91SCole Faust (syscall not in arg_inspection and event_type == 'SYSCALL')): 265*4b9c6d91SCole Faust # Skip SECCOMP records for syscalls that require argument 266*4b9c6d91SCole Faust # inspection. Similarly, skip SYSCALL records for syscalls 267*4b9c6d91SCole Faust # that do not require argument inspection. Technically such 268*4b9c6d91SCole Faust # records wouldn't exist per our setup instructions but audit 269*4b9c6d91SCole Faust # sometimes lets a few records slip through. 270*4b9c6d91SCole Faust au.next_record() 271*4b9c6d91SCole Faust continue 272*4b9c6d91SCole Faust elif event_type == 'SYSCALL': 273*4b9c6d91SCole Faust arg_field_name = f'a{arg_inspection[syscall].arg_index}' 274*4b9c6d91SCole Faust if not _find_field_in_current_record(arg_field_name): 275*4b9c6d91SCole Faust raise ValueError(f'Could not find field "{arg_field_name}"' 276*4b9c6d91SCole Faust f'in event of type {event_type}') 277*4b9c6d91SCole Faust # Intepret the arg field that's under our 'cursor' following the 278*4b9c6d91SCole Faust # find. This may yield a more human friendly name. 279*4b9c6d91SCole Faust # E.g '5401' -> 'TCGETS'. 280*4b9c6d91SCole Faust arg_inspection[syscall].value_set.add(au.interpret_field()) 281*4b9c6d91SCole Faust 282*4b9c6d91SCole Faust syscalls[syscall] += 1 283*4b9c6d91SCole Faust au.next_record() 284*4b9c6d91SCole Faust 285*4b9c6d91SCole Faust 286*4b9c6d91SCole Faustdef main(argv=None): 287*4b9c6d91SCole Faust """Main entrypoint.""" 288*4b9c6d91SCole Faust 289*4b9c6d91SCole Faust if argv is None: 290*4b9c6d91SCole Faust argv = sys.argv[1:] 291*4b9c6d91SCole Faust 292*4b9c6d91SCole Faust opts = parse_args(argv) 293*4b9c6d91SCole Faust 294*4b9c6d91SCole Faust syscalls = collections.defaultdict(int) 295*4b9c6d91SCole Faust 296*4b9c6d91SCole Faust arg_inspection = { 297*4b9c6d91SCole Faust 'socket': ArgInspectionEntry(0, set([])), # int domain 298*4b9c6d91SCole Faust 'ioctl': ArgInspectionEntry(1, set([])), # int request 299*4b9c6d91SCole Faust 'prctl': ArgInspectionEntry(0, set([])), # int option 300*4b9c6d91SCole Faust 'mmap': ArgInspectionEntry(2, set([])), # int prot 301*4b9c6d91SCole Faust 'mmap2': ArgInspectionEntry(2, set([])), # int prot 302*4b9c6d91SCole Faust 'mprotect': ArgInspectionEntry(2, set([])), # int prot 303*4b9c6d91SCole Faust } 304*4b9c6d91SCole Faust 305*4b9c6d91SCole Faust if opts.verbose: 306*4b9c6d91SCole Faust # Print an informational message to stderr in case the filetype detection 307*4b9c6d91SCole Faust # heuristics are wonky. 308*4b9c6d91SCole Faust print('Generating a seccomp policy using these input files:', 309*4b9c6d91SCole Faust file=sys.stderr) 310*4b9c6d91SCole Faust print(f'Strace logs: {opts.traces}', file=sys.stderr) 311*4b9c6d91SCole Faust print(f'Audit logs: {opts.audit_logs}', file=sys.stderr) 312*4b9c6d91SCole Faust 313*4b9c6d91SCole Faust for trace_filename in opts.traces: 314*4b9c6d91SCole Faust parse_trace_file(trace_filename, syscalls, arg_inspection) 315*4b9c6d91SCole Faust 316*4b9c6d91SCole Faust for audit_log in opts.audit_logs: 317*4b9c6d91SCole Faust parse_audit_log(audit_log, opts.audit_comm, syscalls, arg_inspection) 318*4b9c6d91SCole Faust 319*4b9c6d91SCole Faust # Add the basic set if they are not yet present. 320*4b9c6d91SCole Faust basic_set = [ 321*4b9c6d91SCole Faust 'restart_syscall', 'exit', 'exit_group', 'rt_sigreturn', 322*4b9c6d91SCole Faust ] 323*4b9c6d91SCole Faust for basic_syscall in basic_set: 324*4b9c6d91SCole Faust if basic_syscall not in syscalls: 325*4b9c6d91SCole Faust syscalls[basic_syscall] = 1 326*4b9c6d91SCole Faust 327*4b9c6d91SCole Faust # If a frequency file isn't used then sort the syscalls based on frequency 328*4b9c6d91SCole Faust # to make the common case fast (by checking frequent calls earlier). 329*4b9c6d91SCole Faust # Otherwise, sort alphabetically to make it easier for humans to see which 330*4b9c6d91SCole Faust # calls are in use (and if necessary manually add a new syscall to the 331*4b9c6d91SCole Faust # list). 332*4b9c6d91SCole Faust if opts.frequency is None: 333*4b9c6d91SCole Faust sorted_syscalls = list( 334*4b9c6d91SCole Faust x[0] for x in sorted(syscalls.items(), key=lambda pair: pair[1], 335*4b9c6d91SCole Faust reverse=True) 336*4b9c6d91SCole Faust ) 337*4b9c6d91SCole Faust else: 338*4b9c6d91SCole Faust sorted_syscalls = list( 339*4b9c6d91SCole Faust x[0] for x in sorted(syscalls.items(), key=lambda pair: pair[0]) 340*4b9c6d91SCole Faust ) 341*4b9c6d91SCole Faust 342*4b9c6d91SCole Faust print(NOTICE, file=opts.policy) 343*4b9c6d91SCole Faust if opts.frequency is not None: 344*4b9c6d91SCole Faust print(NOTICE, file=opts.frequency) 345*4b9c6d91SCole Faust 346*4b9c6d91SCole Faust for syscall in sorted_syscalls: 347*4b9c6d91SCole Faust if syscall in arg_inspection: 348*4b9c6d91SCole Faust arg_filter = get_seccomp_bpf_filter(syscall, 349*4b9c6d91SCole Faust arg_inspection[syscall]) 350*4b9c6d91SCole Faust else: 351*4b9c6d91SCole Faust arg_filter = ALLOW 352*4b9c6d91SCole Faust print(f'{syscall}: {arg_filter}', file=opts.policy) 353*4b9c6d91SCole Faust if opts.frequency is not None: 354*4b9c6d91SCole Faust print(f'{syscall}: {syscalls[syscall]}', file=opts.frequency) 355*4b9c6d91SCole Faust 356*4b9c6d91SCole Faust 357*4b9c6d91SCole Faustif __name__ == '__main__': 358*4b9c6d91SCole Faust sys.exit(main(sys.argv[1:])) 359