aboutsummaryrefslogtreecommitdiff
path: root/seccomp_tools/mass_seccomp_editor/mass_seccomp_editor.py
diff options
context:
space:
mode:
Diffstat (limited to 'seccomp_tools/mass_seccomp_editor/mass_seccomp_editor.py')
-rwxr-xr-xseccomp_tools/mass_seccomp_editor/mass_seccomp_editor.py273
1 files changed, 0 insertions, 273 deletions
diff --git a/seccomp_tools/mass_seccomp_editor/mass_seccomp_editor.py b/seccomp_tools/mass_seccomp_editor/mass_seccomp_editor.py
deleted file mode 100755
index d8dd7626..00000000
--- a/seccomp_tools/mass_seccomp_editor/mass_seccomp_editor.py
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright 2021 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Script to make mass, CrOS-wide seccomp changes."""
-
-import argparse
-import re
-import subprocess
-import sys
-import shutil
-from typing import Any, Iterable, Optional
-from dataclasses import dataclass, field
-
-# Pre-compiled regexes.
-AMD64_RE = re.compile(r'.*(amd|x86_)64.*\.policy')
-X86_RE = re.compile(r'.*x86.*\.policy')
-AARCH64_RE = re.compile(r'.*a(arch|rm)64.*\.policy')
-ARM_RE = re.compile(r'.*arm(v7)?.*\.policy')
-
-
-@dataclass(frozen=True)
-class Policies:
- """Dataclass to hold lists of policies which match certain types."""
- arm: list[str] = field(default_factory=list)
- x86_64: list[str] = field(default_factory=list)
- x86: list[str] = field(default_factory=list)
- arm64: list[str] = field(default_factory=list)
- none: list[str] = field(default_factory=list)
-
- def to_dict(self) -> dict[str, list[str]]:
- """Convert this class to a dictionary."""
- return {**self.__dict__}
-
-
-def main():
- """Run the program from cmd line"""
- args = parse_args()
- if all(x is None for x in [args.all, args.b64, args.b32, args.none]):
- print('Require at least one of {--all, --b64, --b32, --none}',
- file=sys.stderr)
- sys.exit(1)
- matches, success = find_potential_policy_files(args.packages)
-
- separated = Policies()
-
- for m in matches:
- if AMD64_RE.match(m):
- separated.x86_64.append(m)
- continue
- if X86_RE.match(m):
- separated.x86.append(m)
- continue
- if AARCH64_RE.match(m):
- separated.arm64.append(m)
- continue
- if ARM_RE.match(m):
- separated.arm.append(m)
- continue
- separated.none.append(m)
-
- syscall_lookup_table = _make_syscall_lookup_table(args)
-
- for (type_, val) in separated.to_dict().items():
- for fp in val:
- syscalls = syscall_lookup_table[type_]
- missing = check_missing_syscalls(syscalls, fp)
- if missing is None:
- print(f'E ({type_}) {fp}')
- elif len(missing) == 0:
- print(f'_ ({type_}) {fp}')
- else:
- missing_str = ','.join(missing)
- print(f'M ({type_}) {fp} :: {missing_str}')
-
- if not args.edit:
- sys.exit(0 if success else 2)
-
- for (type_, val) in separated.to_dict().items():
- for fp in val:
- syscalls = syscall_lookup_table[type_]
- if args.force:
- _confirm_add(fp, syscalls, args.yes)
- continue
- missing = check_missing_syscalls(syscalls, fp)
- if missing is None or len(missing) == 0:
- print(f'Already good for {fp} ({type_})')
- else:
- _confirm_add(fp, missing, args.yes)
-
- sys.exit(0 if success else 2)
-
-
-def _make_syscall_lookup_table(args: Any) -> dict[str, list[str]]:
- """Make lookup table, segmented by all/b32/b64/none policies.
-
- Args:
- args: Direct output from parse_args.
-
- Returns:
- dict of syscalls we want to search for in each policy file,
- where the key is the policy file arch, and the value is
- a list of syscalls as strings.
- """
- syscall_lookup_table = Policies().to_dict()
- if args.all:
- split_syscalls = [x.strip() for x in args.all.split(',')]
- for v in syscall_lookup_table.values():
- v.extend(split_syscalls)
- if args.b32:
- split_syscalls = [x.strip() for x in args.b32.split(',')]
- syscall_lookup_table['x86'].extend(split_syscalls)
- syscall_lookup_table['arm'].extend(split_syscalls)
- if args.b64:
- split_syscalls = [x.strip() for x in args.b64.split(',')]
- syscall_lookup_table['x86_64'].extend(split_syscalls)
- syscall_lookup_table['arm64'].extend(split_syscalls)
- if args.none:
- split_syscalls = [x.strip() for x in args.none.split(',')]
- syscall_lookup_table['none'].extend(split_syscalls)
- return syscall_lookup_table
-
-
-def _confirm_add(fp: str, syscalls: Iterable[str], noninteractive=None):
- """Interactive confirmation check you wish to add a syscall.
-
- Args:
- fp: filepath of the file to edit.
- syscalls: list-like of syscalls to add to append to the files.
- noninteractive: Just add the syscalls without asking.
- """
- if noninteractive:
- _update_seccomp(fp, list(syscalls))
- return
- syscalls_str = ','.join(syscalls)
- user_input = input(f'Add {syscalls_str} for {fp}? [y/N]> ')
- if user_input.lower().startswith('y'):
- _update_seccomp(fp, list(syscalls))
- print('Edited!')
- else:
- print(f'Skipping {fp}')
-
-
-def check_missing_syscalls(syscalls: list[str], fp: str) -> Optional[set[str]]:
- """Return which specified syscalls are missing in the given file."""
- missing_syscalls = set(syscalls)
- with open(fp) as f:
- try:
- lines = f.readlines()
- for syscall in syscalls:
- for line in lines:
- if re.match(syscall + r':\s*1', line):
- missing_syscalls.remove(syscall)
- except UnicodeDecodeError:
- return None
- return missing_syscalls
-
-
-def _update_seccomp(fp: str, missing_syscalls: list[str]):
- """Update the seccomp of the file based on the seccomp change type."""
- with open(fp, 'a') as f:
- sorted_syscalls = sorted(missing_syscalls)
- for to_write in sorted_syscalls:
- f.write(to_write + ': 1\n')
-
-
-def _search_cmd(query: str, use_fd=True) -> list[str]:
- if use_fd and shutil.which('fdfind') is not None:
- return [
- 'fdfind',
- '-t',
- 'f',
- '--full-path',
- f'^.*{query}.*\\.policy$',
- ]
- return [
- 'find',
- '.',
- '-regex',
- f'^.*{query}.*\\.policy$',
- '-type',
- 'f',
- ]
-
-
-def find_potential_policy_files(packages: list[str]) -> tuple[list[str], bool]:
- """Find potentially related policy files to the given packages.
-
- Returns:
- (policy_files, successful): A list of policy file paths, and a boolean
- indicating whether all queries were successful in finding at least
- one related policy file.
- """
- all_queries_succeeded = True
- matches = []
- for p in packages:
- # It's quite common that hyphens are translated to underscores
- # and similarly common that underscores are translated to hyphens.
- # We make them agnostic here.
- hyphen_agnostic = re.sub(r'[-_]', '[-_]', p)
- cmd = subprocess.run(
- _search_cmd(hyphen_agnostic),
- stdout=subprocess.PIPE,
- check=True,
- )
- new_matches = [a for a in cmd.stdout.decode('utf-8').split('\n') if a]
- if not new_matches:
- print(f'WARNING: No matches found for {p}', file=sys.stderr)
- all_queries_succeeded = False
- else:
- matches.extend(new_matches)
- return matches, all_queries_succeeded
-
-
-def parse_args() -> Any:
- """Handle command line arguments."""
- parser = argparse.ArgumentParser(
- description='Check for missing syscalls in'
- ' seccomp policy files, or make'
- ' mass seccomp changes.\n\n'
- 'The format of this output follows the template:\n'
- ' status (arch) local/policy/filepath :: syscall,syscall,syscall\n'
- 'Where the status can be "_" for present, "M" for missing,'
- ' or "E" for Error\n\n'
- 'Example:\n'
- ' mass_seccomp_editor.py --all fstatfs --b32 fstatfs64'
- ' modemmanager\n\n'
- 'Exit Codes:\n'
- " '0' for successfully found specific policy files\n"
- " '1' for python-related error.\n"
- " '2' for no matched policy files for a given query.",
- formatter_class=argparse.RawTextHelpFormatter,
- )
- parser.add_argument('packages', nargs='+')
- parser.add_argument(
- '--all',
- type=str,
- metavar='syscalls',
- help='comma separated syscalls to check in all policy files')
- parser.add_argument(
- '--b64',
- type=str,
- metavar='syscalls',
- help='Comma separated syscalls to check in 64bit architectures')
- parser.add_argument(
- '--b32',
- type=str,
- metavar='syscalls',
- help='Comma separated syscalls to check in 32bit architectures')
- parser.add_argument(
- '--none',
- type=str,
- metavar='syscalls',
- help='Comma separated syscalls to check in unknown architectures')
- parser.add_argument('--edit',
- action='store_true',
- help='Make changes to the listed files,'
- ' rather than just printing out what is missing')
- parser.add_argument('-y',
- '--yes',
- action='store_true',
- help='Say "Y" to all interactive checks')
- parser.add_argument('--force',
- action='store_true',
- help='Edit all files, regardless of missing status.'
- ' Does nothing without --edit.')
- return parser.parse_args()
-
-
-if __name__ == '__main__':
- main()