2021-10-13 20:25:03 +00:00
|
|
|
#!/bin/sh -eu
|
|
|
|
#
|
passt: Relicense to GPL 2.0, or any later version
In practical terms, passt doesn't benefit from the additional
protection offered by the AGPL over the GPL, because it's not
suitable to be executed over a computer network.
Further, restricting the distribution under the version 3 of the GPL
wouldn't provide any practical advantage either, as long as the passt
codebase is concerned, and might cause unnecessary compatibility
dilemmas.
Change licensing terms to the GNU General Public License Version 2,
or any later version, with written permission from all current and
past contributors, namely: myself, David Gibson, Laine Stump, Andrea
Bolognani, Paul Holzinger, Richard W.M. Jones, Chris Kuhn, Florian
Weimer, Giuseppe Scrivano, Stefan Hajnoczi, and Vasiliy Ulyanov.
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2023-04-05 18:11:44 +00:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2021-10-13 20:25:03 +00:00
|
|
|
#
|
|
|
|
# PASST - Plug A Simple Socket Transport
|
|
|
|
# for qemu/UNIX domain socket mode
|
|
|
|
#
|
|
|
|
# PASTA - Pack A Subtle Tap Abstraction
|
|
|
|
# for network namespace/tap device mode
|
|
|
|
#
|
2021-10-20 23:21:26 +00:00
|
|
|
# seccomp.sh - Build seccomp profiles from "#syscalls[:PROFILE]" code comments
|
2021-10-13 20:25:03 +00:00
|
|
|
#
|
|
|
|
# Copyright (c) 2021 Red Hat GmbH
|
|
|
|
# Author: Stefano Brivio <sbrivio@redhat.com>
|
|
|
|
|
|
|
|
TMP="$(mktemp)"
|
2022-06-14 05:12:21 +00:00
|
|
|
IN="$@"
|
2023-06-21 03:06:37 +00:00
|
|
|
OUT="$(mktemp)"
|
2021-10-13 20:25:03 +00:00
|
|
|
|
2023-02-27 23:53:01 +00:00
|
|
|
[ -z "${ARCH}" ] && ARCH="$(uname -m)"
|
|
|
|
[ -z "${CC}" ] && CC="cc"
|
|
|
|
|
2024-11-05 23:25:24 +00:00
|
|
|
AUDIT_ARCH="AUDIT_ARCH_$(echo ${ARCH} | tr [a-z] [A-Z] \
|
|
|
|
| sed 's/^ARM.*/ARM/' \
|
|
|
|
| sed 's/I[456]86/I386/' \
|
|
|
|
| sed 's/PPC64/PPC/' \
|
|
|
|
| sed 's/PPCLE/PPC64LE/' \
|
|
|
|
| sed 's/MIPS64EL/MIPSEL64/' \
|
|
|
|
| sed 's/HPPA/PARISC/' \
|
|
|
|
| sed 's/SH4/SH/')"
|
|
|
|
|
2022-01-25 18:07:05 +00:00
|
|
|
HEADER="/* This file was automatically generated by $(basename ${0}) */
|
|
|
|
|
|
|
|
#ifndef AUDIT_ARCH_PPC64LE
|
|
|
|
#define AUDIT_ARCH_PPC64LE (AUDIT_ARCH_PPC64 | __AUDIT_ARCH_LE)
|
|
|
|
#endif"
|
2021-10-13 20:25:03 +00:00
|
|
|
|
|
|
|
# Prefix for each profile: check that 'arch' in seccomp_data is matching
|
|
|
|
PRE='
|
|
|
|
struct sock_filter filter_@PROFILE@[] = {
|
2024-03-21 04:57:38 +00:00
|
|
|
/* cppcheck-suppress [badBitmaskCheck, unmatchedSuppression] */
|
2021-10-13 20:25:03 +00:00
|
|
|
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
|
|
|
|
(offsetof(struct seccomp_data, arch))),
|
2024-11-05 23:25:24 +00:00
|
|
|
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, @AUDIT_ARCH@, 0, @KILL@),
|
2024-03-21 04:57:38 +00:00
|
|
|
/* cppcheck-suppress [badBitmaskCheck, unmatchedSuppression] */
|
2021-10-13 20:25:03 +00:00
|
|
|
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
|
|
|
|
(offsetof(struct seccomp_data, nr))),
|
|
|
|
|
|
|
|
'
|
|
|
|
|
|
|
|
# Suffix for each profile: return actions
|
|
|
|
POST=' BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
|
|
|
|
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
|
|
|
|
};
|
|
|
|
'
|
|
|
|
|
|
|
|
# Syscall, @NR@: number, @ALLOW@: offset to RET_ALLOW, @NAME@: syscall name
|
|
|
|
CALL=' BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, @NR@, @ALLOW@, 0), /* @NAME@ */'
|
|
|
|
|
|
|
|
# Binary search tree node or leaf, @NR@: value, @R@: right jump, @L@: left jump
|
|
|
|
BST=' BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, @NR@, @R@, @L@),'
|
|
|
|
|
2021-10-20 23:21:26 +00:00
|
|
|
# cleanup() - Remove temporary file if it exists
|
|
|
|
cleanup() {
|
2023-06-21 03:06:37 +00:00
|
|
|
rm -f "${TMP}" "${OUT}"
|
2021-10-20 23:21:26 +00:00
|
|
|
}
|
|
|
|
trap "cleanup" EXIT
|
|
|
|
|
2021-10-13 20:25:03 +00:00
|
|
|
# sub() - Substitute in-place file line with processed template line
|
|
|
|
# $1: Line number
|
2021-10-20 23:21:26 +00:00
|
|
|
# $2: Template name (variable name)
|
2021-10-13 20:25:03 +00:00
|
|
|
# $@: Replacement for @KEY@ in the form KEY:value
|
|
|
|
sub() {
|
|
|
|
IFS=
|
|
|
|
__line_no="${1}"
|
|
|
|
__template="$(eval printf '%s' "\${${2}}")"
|
|
|
|
shift; shift
|
|
|
|
|
|
|
|
sed -i "${__line_no}s#.*#${__template}#" "${TMP}"
|
|
|
|
|
2022-01-25 18:21:31 +00:00
|
|
|
IFS=' '
|
2021-10-13 20:25:03 +00:00
|
|
|
for __def in ${@}; do
|
|
|
|
__key="@${__def%%:*}@"
|
|
|
|
__value="${__def#*:}"
|
|
|
|
sed -i "${__line_no}s/${__key}/${__value}/" "${TMP}"
|
|
|
|
done
|
|
|
|
unset IFS
|
|
|
|
}
|
|
|
|
|
|
|
|
# finish() - Finalise header file from temporary files with prefix and suffix
|
|
|
|
# $1: Variable name of prefix
|
|
|
|
# $@: Replacements for prefix variable
|
|
|
|
finish() {
|
|
|
|
IFS=
|
|
|
|
__out="$(eval printf '%s' "\${${1}}")"
|
|
|
|
shift
|
|
|
|
|
2022-01-25 18:21:31 +00:00
|
|
|
IFS=' '
|
2021-10-13 20:25:03 +00:00
|
|
|
for __def in ${@}; do
|
|
|
|
__key="@${__def%%:*}@"
|
|
|
|
__value="${__def#*:}"
|
|
|
|
__out="$(printf '%s' "${__out}" | sed "s#${__key}#${__value}#")"
|
|
|
|
done
|
|
|
|
|
|
|
|
printf '%s\n' "${__out}" >> "${OUT}"
|
|
|
|
cat "${TMP}" >> "${OUT}"
|
|
|
|
rm "${TMP}"
|
|
|
|
printf '%s' "${POST}" >> "${OUT}"
|
|
|
|
unset IFS
|
|
|
|
}
|
|
|
|
|
|
|
|
# log2() - Binary logarithm
|
|
|
|
# $1: Operand
|
|
|
|
log2() {
|
|
|
|
__x=-1
|
|
|
|
__y=${1}
|
|
|
|
while [ ${__y} -gt 0 ]; do : $((__y >>= 1)); __x=$((__x + 1)); done
|
|
|
|
echo ${__x}
|
|
|
|
}
|
|
|
|
|
2022-01-25 18:21:31 +00:00
|
|
|
# syscall_nr - Get syscall number from compiler
|
2021-10-20 23:21:26 +00:00
|
|
|
# $1: Name of syscall
|
2022-01-25 18:21:31 +00:00
|
|
|
syscall_nr() {
|
|
|
|
__in="$(printf "#include <asm-generic/unistd.h>\n#include <sys/syscall.h>\n__NR_%s" ${1})"
|
2023-02-27 23:53:01 +00:00
|
|
|
__out="$(echo "${__in}" | ${CC} -E -xc - -o - | tail -1)"
|
2021-10-20 23:21:26 +00:00
|
|
|
[ "${__out}" = "__NR_$1" ] && return 1
|
2022-02-26 22:29:18 +00:00
|
|
|
|
|
|
|
# Output might be in the form "(x + y)" (seen on armv6l, armv7l)
|
|
|
|
__out="$(eval echo $((${__out})))"
|
2021-10-20 23:21:26 +00:00
|
|
|
echo "${__out}"
|
|
|
|
}
|
|
|
|
|
2022-01-25 18:21:31 +00:00
|
|
|
filter() {
|
|
|
|
__filtered=
|
|
|
|
for __c in ${@}; do
|
|
|
|
__arch_match=0
|
|
|
|
case ${__c} in
|
|
|
|
*:*)
|
|
|
|
case ${__c} in
|
2023-02-27 23:53:01 +00:00
|
|
|
${ARCH}:*)
|
2022-01-25 18:21:31 +00:00
|
|
|
__arch_match=1
|
|
|
|
__c=${__c##*:}
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
;;
|
|
|
|
*)
|
|
|
|
__arch_match=1
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
[ ${__arch_match} -eq 0 ] && continue
|
|
|
|
|
|
|
|
IFS='| '
|
|
|
|
__found=0
|
|
|
|
for __name in ${__c}; do
|
|
|
|
syscall_nr "${__name}" >/dev/null && __found=1 && break
|
|
|
|
done
|
|
|
|
unset IFS
|
|
|
|
|
|
|
|
if [ ${__found} -eq 0 ]; then
|
|
|
|
echo
|
|
|
|
echo "Warning: no syscall number for ${__c}" >&2
|
|
|
|
echo " none of these syscalls will be allowed" >&2
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
|
|
|
|
__filtered="${__filtered} ${__name}"
|
|
|
|
done
|
|
|
|
|
|
|
|
echo "${__filtered}" | tr ' ' '\n' | sort -u
|
|
|
|
}
|
|
|
|
|
2021-10-13 20:25:03 +00:00
|
|
|
# gen_profile() - Build struct sock_filter for a single profile
|
|
|
|
# $1: Profile name
|
|
|
|
# $@: Names of allowed system calls, amount padded to next power of two
|
|
|
|
gen_profile() {
|
|
|
|
__profile="${1}"
|
|
|
|
shift
|
|
|
|
|
|
|
|
__statements_calls=${#}
|
|
|
|
__bst_levels=$(log2 $(( __statements_calls / 4 )) )
|
|
|
|
__statements_bst=$(( __statements_calls / 4 - 1 ))
|
|
|
|
__statements=$((__statements_calls + __statements_bst))
|
|
|
|
|
|
|
|
for __i in $(seq 1 ${__statements_bst} ); do
|
|
|
|
echo -1 >> "${TMP}"
|
|
|
|
done
|
|
|
|
for __i in $(seq 1 ${__statements_calls} ); do
|
2021-10-20 23:21:26 +00:00
|
|
|
__syscall_name="$(eval echo \${${__i}})"
|
2022-01-25 18:21:31 +00:00
|
|
|
if ! syscall_nr ${__syscall_name} >> "${TMP}"; then
|
2021-10-20 23:21:26 +00:00
|
|
|
echo "Cannot get syscall number for ${__syscall_name}"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
eval __syscall_nr_$(tail -1 "${TMP}")="${__syscall_name}"
|
2021-10-13 20:25:03 +00:00
|
|
|
done
|
|
|
|
sort -go "${TMP}" "${TMP}"
|
|
|
|
|
|
|
|
__distance=$(( __statements_calls / 2 ))
|
|
|
|
__level_nodes=1
|
|
|
|
__ll=0
|
|
|
|
__line=1
|
|
|
|
for __level in $(seq 1 $(( __bst_levels - 1 )) ); do
|
|
|
|
# Nodes
|
|
|
|
__cmp_pos=${__distance}
|
|
|
|
|
|
|
|
for __node in $(seq 1 ${__level_nodes}); do
|
|
|
|
__cmp_line=$(( __statements_bst + __cmp_pos ))
|
|
|
|
__lr=$(( __ll + 1 ))
|
|
|
|
__nr="$(sed -n ${__cmp_line}p "${TMP}")"
|
|
|
|
|
|
|
|
sub ${__line} BST "NR:${__nr}" "L:${__ll}" "R:${__lr}"
|
|
|
|
|
|
|
|
__ll=${__lr}
|
|
|
|
__line=$(( __line + 1 ))
|
|
|
|
__cmp_pos=$(( __cmp_pos + __distance * 2 ))
|
|
|
|
done
|
|
|
|
|
|
|
|
__distance=$(( __distance / 2 ))
|
|
|
|
__level_nodes=$(( __level_nodes * 2 ))
|
|
|
|
done
|
|
|
|
|
|
|
|
# Leaves
|
|
|
|
__ll=$(( __level_nodes - 1 ))
|
|
|
|
__lr=$(( __ll + __distance - 1 ))
|
|
|
|
__cmp_pos=${__distance}
|
|
|
|
|
|
|
|
for __leaf in $(seq 1 ${__level_nodes}); do
|
|
|
|
__cmp_line=$(( __statements_bst + __cmp_pos ))
|
|
|
|
__nr="$(sed -n ${__cmp_line}p "${TMP}")"
|
|
|
|
sub ${__line} BST "NR:${__nr}" "L:${__ll}" "R:${__lr}"
|
|
|
|
|
|
|
|
__ll=$(( __lr + __distance - 1 ))
|
|
|
|
__lr=$(( __ll + __distance))
|
|
|
|
__line=$(( __line + 1 ))
|
|
|
|
__cmp_pos=$(( __cmp_pos + __distance * 2 ))
|
|
|
|
done
|
|
|
|
|
|
|
|
# Calls
|
|
|
|
for __i in $(seq $(( __statements_bst + 1 )) ${__statements}); do
|
|
|
|
__nr="$(sed -n ${__i}p "${TMP}")"
|
2021-10-20 23:21:26 +00:00
|
|
|
eval __name="\${__syscall_nr_${__nr}}"
|
2021-10-13 20:25:03 +00:00
|
|
|
__allow=$(( __statements - __i + 1 ))
|
|
|
|
sub ${__i} CALL "NR:${__nr}" "NAME:${__name}" "ALLOW:${__allow}"
|
|
|
|
done
|
|
|
|
|
2024-11-05 23:25:24 +00:00
|
|
|
finish PRE "PROFILE:${__profile}" "KILL:$(( __statements + 1))" \
|
|
|
|
"AUDIT_ARCH:${AUDIT_ARCH}"
|
2021-10-13 20:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
printf '%s\n' "${HEADER}" > "${OUT}"
|
2022-06-14 05:12:21 +00:00
|
|
|
__profiles="$(sed -n 's/[\t ]*\*[\t ]*#syscalls:\([^ ]*\).*/\1/p' ${IN} | sort -u)"
|
2021-10-13 20:25:03 +00:00
|
|
|
for __p in ${__profiles}; do
|
2022-06-14 05:12:21 +00:00
|
|
|
__calls="$(sed -n 's/[\t ]*\*[\t ]*#syscalls\(:'"${__p}"'\|\)[\t ]\{1,\}\(.*\)/\2/p' ${IN})"
|
2022-03-15 19:16:13 +00:00
|
|
|
__calls="${__calls} ${EXTRA_SYSCALLS:-}"
|
2022-01-25 18:21:31 +00:00
|
|
|
__calls="$(filter ${__calls})"
|
2024-08-27 06:23:41 +00:00
|
|
|
|
|
|
|
cols="$(stty -a | sed -n 's/.*columns \([0-9]*\).*/\1/p' || :)" 2>/dev/null
|
|
|
|
case $cols in [0-9]*) col_args="-w ${cols}";; *) col_args="";; esac
|
|
|
|
echo "seccomp profile ${__p} allows: ${__calls}" | tr '\n' ' ' | fmt -t ${col_args}
|
2021-10-13 20:25:03 +00:00
|
|
|
|
|
|
|
# Pad here to keep gen_profile() "simple"
|
|
|
|
__count=0
|
|
|
|
for __c in ${__calls}; do __count=$(( __count + 1 )); done
|
|
|
|
__padded=$(( 1 << (( $(log2 ${__count}) + 1 )) ))
|
|
|
|
for __i in $( seq ${__count} $(( __padded - 1 )) ); do
|
2022-01-25 18:21:31 +00:00
|
|
|
__calls="${__calls} read"
|
2021-10-13 20:25:03 +00:00
|
|
|
done
|
|
|
|
|
|
|
|
gen_profile "${__p}" ${__calls}
|
|
|
|
done
|
2023-06-21 03:06:37 +00:00
|
|
|
|
|
|
|
mv "${OUT}" seccomp.h
|