diff options
author | Jorge Lucangeli Obes <jorgelo@google.com> | 2016-02-17 12:40:34 -0800 |
---|---|---|
committer | Jorge Lucangeli Obes <jorgelo@google.com> | 2016-02-17 12:40:34 -0800 |
commit | 6482e8d2d70407a190e7eea4aeb895da0ed19cb1 (patch) | |
tree | b19d785cae4795373dea4de317c6e4a2c8ef7f71 | |
parent | 768d42b6675444f158e7a03df688ec788e07c567 (diff) | |
download | minijail-6482e8d2d70407a190e7eea4aeb895da0ed19cb1.tar.gz |
Add a syscall filter generation script.brillo-m10-releasebrillo-m10-dev
Many people have found this script useful to bootstrap their seccomp
filter policies. Add it to the repo instead of keeping it elsewhere.
Bug: None
Change-Id: I1ec0c25eb52aac1542f710a5acdb7d2616d075d6
-rwxr-xr-x | tools/generate_seccomp_policy.py | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/tools/generate_seccomp_policy.py b/tools/generate_seccomp_policy.py new file mode 100755 index 0000000..0b51e71 --- /dev/null +++ b/tools/generate_seccomp_policy.py @@ -0,0 +1,169 @@ +#!/usr/bin/python +# +# Copyright (C) 2016 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. +# +# This script will take any number of trace files generated by strace(1) +# and output a system call filtering policy suitable for use with Minijail. + +from collections import namedtuple +import sys + +NOTICE = """# Copyright (C) 2016 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. +""" + +ALLOW = "%s: 1" + +SOCKETCALLS = ["accept", "bind", "connect", "getpeername", "getsockname", + "getsockopt", "listen", "recv", "recvfrom", "recvmsg", "send", + "sendmsg", "sendto", "setsockopt", "shutdown", "socket", + "socketpair"] + +# /* Protocol families. */ +# #define PF_UNSPEC 0 /* Unspecified. */ +# #define PF_LOCAL 1 /* Local to host (pipes and file-domain). */ +# #define PF_UNIX PF_LOCAL /* POSIX name for PF_LOCAL. */ +# #define PF_FILE PF_LOCAL /* Another non-standard name for PF_LOCAL. */ +# #define PF_INET 2 /* IP protocol family. */ +# #define PF_AX25 3 /* Amateur Radio AX.25. */ +# #define PF_IPX 4 /* Novell Internet Protocol. */ +# #define PF_APPLETALK 5 /* Appletalk DDP. */ +# #define PF_NETROM 6 /* Amateur radio NetROM. */ +# #define PF_BRIDGE 7 /* Multiprotocol bridge. */ +# #define PF_ATMPVC 8 /* ATM PVCs. */ +# #define PF_X25 9 /* Reserved for X.25 project. */ +# #define PF_INET6 10 /* IP version 6. */ +# #define PF_ROSE 11 /* Amateur Radio X.25 PLP. */ +# #define PF_DECnet 12 /* Reserved for DECnet project. */ +# #define PF_NETBEUI 13 /* Reserved for 802.2LLC project. */ +# #define PF_SECURITY 14 /* Security callback pseudo AF. */ +# #define PF_KEY 15 /* PF_KEY key management API. */ +# #define PF_NETLINK 16 + +ArgInspectionEntry = namedtuple("ArgInspectionEntry", "arg_index value_set") + + +def usage(argv): + print "%s <trace file> [trace files...]" % argv[0] + + +def main(traces): + syscalls = {} + + uses_socketcall = False + + basic_set = ["restart_syscall", "exit", "exit_group", + "rt_sigreturn"] + frequent_set = [] + + syscall_sets = {} + syscall_set_list = [["sigreturn", "rt_sigreturn"], + ["sigaction", "rt_sigaction"], + ["sigprocmask", "rt_sigprocmask"], + ["open", "openat"], + ["mmap", "mremap"], + ["mmap2", "mremap"]] + + arg_inspection = { + "socket": ArgInspectionEntry(0, set([])), # int domain + "ioctl": ArgInspectionEntry(1, set([])), # int request + "prctl": ArgInspectionEntry(0, set([])) # int option + } + + for syscall_list in syscall_set_list: + for syscall in syscall_list: + other_syscalls = syscall_list[:] + other_syscalls.remove(syscall) + syscall_sets[syscall] = other_syscalls + + for trace_filename in traces: + if "i386" in trace_filename or ("x86" in trace_filename and + "64" not in trace_filename): + uses_socketcall = True + + trace_file = open(trace_filename) + for line in trace_file: + if "---" in line or '(' not in line: + continue + + syscall, args = line.strip().split('(', 1) + if uses_socketcall and syscall in SOCKETCALLS: + syscall = "socketcall" + + if syscall in syscalls: + syscalls[syscall] += 1 + else: + syscalls[syscall] = 1 + + args = [arg.strip() for arg in args.split(')', 1)[0].split(',')] + + if syscall in arg_inspection: + arg_value = args[arg_inspection[syscall].arg_index] + arg_inspection[syscall].value_set.add(arg_value) + + sorted_syscalls = list(zip(*sorted(syscalls.iteritems(), + key=lambda pair: pair[1], + reverse=True))[0]) + + print NOTICE + + # Add frequent syscalls first. + for frequent_syscall in frequent_set: + sorted_syscalls.remove(frequent_syscall) + + all_syscalls = frequent_set + sorted_syscalls + + # Add the basic set once the frequency drops below 2. + below_ten_index = -1 + for sorted_syscall in sorted_syscalls: + if syscalls[sorted_syscall] < 2: + below_ten_index = all_syscalls.index(sorted_syscall) + break + + first_half = all_syscalls[:below_ten_index] + for basic_syscall in basic_set: + if basic_syscall not in all_syscalls: + first_half.append(basic_syscall) + + all_syscalls = first_half + all_syscalls[below_ten_index:] + + for syscall in all_syscalls: + if syscall in arg_inspection: + arg_index = arg_inspection[syscall].arg_index + arg_values = arg_inspection[syscall].value_set + arg_filter = " || ".join(["arg%d == %s" % (arg_index, arg_value) + for arg_value in arg_values]) + print syscall + ": " + arg_filter + else: + print ALLOW % syscall + + +if __name__ == "__main__": + if len(sys.argv) < 2: + usage(sys.argv) + sys.exit(1) + + main(sys.argv[1:]) |