aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJorge Lucangeli Obes <jorgelo@google.com>2016-02-17 12:40:34 -0800
committerJorge Lucangeli Obes <jorgelo@google.com>2016-02-17 12:40:34 -0800
commit6482e8d2d70407a190e7eea4aeb895da0ed19cb1 (patch)
treeb19d785cae4795373dea4de317c6e4a2c8ef7f71
parent768d42b6675444f158e7a03df688ec788e07c567 (diff)
downloadminijail-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-xtools/generate_seccomp_policy.py169
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:])