169 lines
6.2 KiB
Python
Executable file
169 lines
6.2 KiB
Python
Executable file
#!/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:])
|