#!/bin/bash

## Copyright (C) 2012 - 2023 ENCRYPTED SUPPORT LP <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

set -e

seccomp_filter_path="/var/cache/sandbox-app-launcher-autogenerated/seccomp-filter"
whitelist_file="${@}"

if [ "${whitelist_file}" = "" ]; then
  echo "ERROR: No file was specified."
  exit 1
fi

if ! [ -f "${whitelist_file}" ]; then
  echo "ERROR: File does not exist: '${whitelist_file}'"
  exit 1
fi

generate_filter() {
  while read -r syscall; do
    actual_syscall=""
    arg_num=""
    syscall_arg=""
    arg_rule=""

    if [[ "${syscall}" =~ ^# ]]; then
      continue
    fi

    if [ "${syscall}" = "" ]; then
      continue
    fi

    if ! [[ "${syscall}" =~ [0-9a-zA-Z/] ]]; then
      echo "ERROR: Entry contains invalid character: '${syscall}'"
      continue
    fi

    if ! read -r actual_syscall arg_num syscall_arg <<< "${syscall}"; then
      echo "ERROR: Cannot parse: '${syscall}'"
      continue
    fi

    if [ "${actual_syscall}" = "@include" ]; then
      generate_filter "${arg_num}"
      continue
    fi

    if [ "${arg_num}" = "" ]; then
      whitelisted_syscalls+="  ALLOW_SYSCALL (${syscall});
"
      continue
    fi

    if ! [ "${arg_num}" = "0" ] && ! [ "${arg_num}" = "1" ] && ! [ "${arg_num}" = "2" ]; then
      echo "ERROR: Not a valid argument number: ${arg_num}"
      continue
    fi

    if [ "${actual_syscall}" = "ioctl" ]; then
      ## TODO: Can we remove this exception without opening up holes?
      whitelisted_syscalls+="  ALLOW_IOCTL (${syscall_arg});
"
    else
      whitelisted_syscalls+="  ALLOW_ARG${arg_num} (${actual_syscall}, ${syscall_arg});
"
    fi
  done < "${1}"
}

generate_filter "${whitelist_file}"

echo "#include <seccomp.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/shm.h>
#include <linux/random.h>
#include <linux/vt.h>
#include <linux/fs.h>
#include <linux/wireless.h>

#define ALLOW_SYSCALL(call) { if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(call), 0) < 0) goto out; }
#define ALLOW_IOCTL(call) { if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_A1 (SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int) call), 0) < 0) goto out; }

#define ALLOW_ARG0(call, arg) { if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(call), 1, SCMP_A0(SCMP_CMP_EQ, arg), 0) < 0) goto out; }
#define ALLOW_ARG1(call, arg) { if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(call), 1, SCMP_A1(SCMP_CMP_EQ, arg), 0) < 0) goto out; }
#define ALLOW_ARG2(call, arg) { if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(call), 1, SCMP_A2(SCMP_CMP_EQ, arg), 0) < 0) goto out; }

int main(int argc, char *argv[])
{
  int rc = -1;
  scmp_filter_ctx ctx;
  int filter_fd;
  char *filter_path = \"${seccomp_filter_path}.bpf\";

  ctx = seccomp_init(SCMP_ACT_KILL);
  if (ctx == NULL)
      goto out;

  /* Whitelist of syscalls */
${whitelisted_syscalls}
  filter_fd = open(filter_path, O_CREAT | O_WRONLY, 0644);
  if (filter_fd == -1) {
      rc = -errno;
      goto out;
  }

  rc = seccomp_export_bpf(ctx, filter_fd);
  if (rc < 0) {
      close(filter_fd);
      goto out;
  }
  close(filter_fd);


 out:
    seccomp_release(ctx);
    return -rc;
}"
