xref: /linux/tools/testing/selftests/exec/check-exec-tests.sh (revision 21266b8df5224c4f677acf9f353eecc9094731f0)
1*2a69962bSMickaël Salaün#!/usr/bin/env bash
2*2a69962bSMickaël Salaün# SPDX-License-Identifier: GPL-2.0
3*2a69962bSMickaël Salaün#
4*2a69962bSMickaël Salaün# Test the "inc" interpreter.
5*2a69962bSMickaël Salaün#
6*2a69962bSMickaël Salaün# See include/uapi/linux/securebits.h, include/uapi/linux/fcntl.h and
7*2a69962bSMickaël Salaün# samples/check-exec/inc.c
8*2a69962bSMickaël Salaün#
9*2a69962bSMickaël Salaün# Copyright © 2024 Microsoft Corporation
10*2a69962bSMickaël Salaün
11*2a69962bSMickaël Salaünset -u -e -o pipefail
12*2a69962bSMickaël Salaün
13*2a69962bSMickaël SalaünEXPECTED_OUTPUT="1"
14*2a69962bSMickaël Salaünexec 2>/dev/null
15*2a69962bSMickaël Salaün
16*2a69962bSMickaël SalaünDIR="$(dirname $(readlink -f "$0"))"
17*2a69962bSMickaël Salaünsource "${DIR}"/../kselftest/ktap_helpers.sh
18*2a69962bSMickaël Salaün
19*2a69962bSMickaël Salaünexec_direct() {
20*2a69962bSMickaël Salaün	local expect="$1"
21*2a69962bSMickaël Salaün	local script="$2"
22*2a69962bSMickaël Salaün	shift 2
23*2a69962bSMickaël Salaün	local ret=0
24*2a69962bSMickaël Salaün	local out
25*2a69962bSMickaël Salaün
26*2a69962bSMickaël Salaün	# Updates PATH for `env` to execute the `inc` interpreter.
27*2a69962bSMickaël Salaün	out="$(PATH="." "$@" "${script}")" || ret=$?
28*2a69962bSMickaël Salaün
29*2a69962bSMickaël Salaün	if [[ ${ret} -ne ${expect} ]]; then
30*2a69962bSMickaël Salaün		echo "ERROR: Wrong expectation for direct file execution: ${ret}"
31*2a69962bSMickaël Salaün		return 1
32*2a69962bSMickaël Salaün	fi
33*2a69962bSMickaël Salaün	if [[ ${ret} -eq 0 && "${out}" != "${EXPECTED_OUTPUT}" ]]; then
34*2a69962bSMickaël Salaün		echo "ERROR: Wrong output for direct file execution: ${out}"
35*2a69962bSMickaël Salaün		return 1
36*2a69962bSMickaël Salaün	fi
37*2a69962bSMickaël Salaün}
38*2a69962bSMickaël Salaün
39*2a69962bSMickaël Salaünexec_indirect() {
40*2a69962bSMickaël Salaün	local expect="$1"
41*2a69962bSMickaël Salaün	local script="$2"
42*2a69962bSMickaël Salaün	shift 2
43*2a69962bSMickaël Salaün	local ret=0
44*2a69962bSMickaël Salaün	local out
45*2a69962bSMickaël Salaün
46*2a69962bSMickaël Salaün	# Script passed as argument.
47*2a69962bSMickaël Salaün	out="$("$@" ./inc "${script}")" || ret=$?
48*2a69962bSMickaël Salaün
49*2a69962bSMickaël Salaün	if [[ ${ret} -ne ${expect} ]]; then
50*2a69962bSMickaël Salaün		echo "ERROR: Wrong expectation for indirect file execution: ${ret}"
51*2a69962bSMickaël Salaün		return 1
52*2a69962bSMickaël Salaün	fi
53*2a69962bSMickaël Salaün	if [[ ${ret} -eq 0 && "${out}" != "${EXPECTED_OUTPUT}" ]]; then
54*2a69962bSMickaël Salaün		echo "ERROR: Wrong output for indirect file execution: ${out}"
55*2a69962bSMickaël Salaün		return 1
56*2a69962bSMickaël Salaün	fi
57*2a69962bSMickaël Salaün}
58*2a69962bSMickaël Salaün
59*2a69962bSMickaël Salaünexec_stdin_reg() {
60*2a69962bSMickaël Salaün	local expect="$1"
61*2a69962bSMickaël Salaün	local script="$2"
62*2a69962bSMickaël Salaün	shift 2
63*2a69962bSMickaël Salaün	local ret=0
64*2a69962bSMickaël Salaün	local out
65*2a69962bSMickaël Salaün
66*2a69962bSMickaël Salaün	# Executing stdin must be allowed if the related file is executable.
67*2a69962bSMickaël Salaün	out="$("$@" ./inc -i < "${script}")" || ret=$?
68*2a69962bSMickaël Salaün
69*2a69962bSMickaël Salaün	if [[ ${ret} -ne ${expect} ]]; then
70*2a69962bSMickaël Salaün		echo "ERROR: Wrong expectation for stdin regular file execution: ${ret}"
71*2a69962bSMickaël Salaün		return 1
72*2a69962bSMickaël Salaün	fi
73*2a69962bSMickaël Salaün	if [[ ${ret} -eq 0 && "${out}" != "${EXPECTED_OUTPUT}" ]]; then
74*2a69962bSMickaël Salaün		echo "ERROR: Wrong output for stdin regular file execution: ${out}"
75*2a69962bSMickaël Salaün		return 1
76*2a69962bSMickaël Salaün	fi
77*2a69962bSMickaël Salaün}
78*2a69962bSMickaël Salaün
79*2a69962bSMickaël Salaünexec_stdin_pipe() {
80*2a69962bSMickaël Salaün	local expect="$1"
81*2a69962bSMickaël Salaün	shift
82*2a69962bSMickaël Salaün	local ret=0
83*2a69962bSMickaël Salaün	local out
84*2a69962bSMickaël Salaün
85*2a69962bSMickaël Salaün	# A pipe is not executable.
86*2a69962bSMickaël Salaün	out="$(cat script-exec.inc | "$@" ./inc -i)" || ret=$?
87*2a69962bSMickaël Salaün
88*2a69962bSMickaël Salaün	if [[ ${ret} -ne ${expect} ]]; then
89*2a69962bSMickaël Salaün		echo "ERROR: Wrong expectation for stdin pipe execution: ${ret}"
90*2a69962bSMickaël Salaün		return 1
91*2a69962bSMickaël Salaün	fi
92*2a69962bSMickaël Salaün}
93*2a69962bSMickaël Salaün
94*2a69962bSMickaël Salaünexec_argument() {
95*2a69962bSMickaël Salaün	local expect="$1"
96*2a69962bSMickaël Salaün	local ret=0
97*2a69962bSMickaël Salaün	shift
98*2a69962bSMickaël Salaün	local out
99*2a69962bSMickaël Salaün
100*2a69962bSMickaël Salaün	# Script not coming from a file must not be executed.
101*2a69962bSMickaël Salaün	out="$("$@" ./inc -c "$(< script-exec.inc)")" || ret=$?
102*2a69962bSMickaël Salaün
103*2a69962bSMickaël Salaün	if [[ ${ret} -ne ${expect} ]]; then
104*2a69962bSMickaël Salaün		echo "ERROR: Wrong expectation for arbitrary argument execution: ${ret}"
105*2a69962bSMickaël Salaün		return 1
106*2a69962bSMickaël Salaün	fi
107*2a69962bSMickaël Salaün	if [[ ${ret} -eq 0 && "${out}" != "${EXPECTED_OUTPUT}" ]]; then
108*2a69962bSMickaël Salaün		echo "ERROR: Wrong output for arbitrary argument execution: ${out}"
109*2a69962bSMickaël Salaün		return 1
110*2a69962bSMickaël Salaün	fi
111*2a69962bSMickaël Salaün}
112*2a69962bSMickaël Salaün
113*2a69962bSMickaël Salaünexec_interactive() {
114*2a69962bSMickaël Salaün	exec_stdin_pipe "$@"
115*2a69962bSMickaël Salaün	exec_argument "$@"
116*2a69962bSMickaël Salaün}
117*2a69962bSMickaël Salaün
118*2a69962bSMickaël Salaünktap_test() {
119*2a69962bSMickaël Salaün	ktap_test_result "$*" "$@"
120*2a69962bSMickaël Salaün}
121*2a69962bSMickaël Salaün
122*2a69962bSMickaël Salaünktap_print_header
123*2a69962bSMickaël Salaünktap_set_plan 28
124*2a69962bSMickaël Salaün
125*2a69962bSMickaël Salaün# Without secbit configuration, nothing is changed.
126*2a69962bSMickaël Salaün
127*2a69962bSMickaël Salaünktap_print_msg "By default, executable scripts are allowed to be interpreted and executed."
128*2a69962bSMickaël Salaünktap_test exec_direct 0 script-exec.inc
129*2a69962bSMickaël Salaünktap_test exec_indirect 0 script-exec.inc
130*2a69962bSMickaël Salaün
131*2a69962bSMickaël Salaünktap_print_msg "By default, executable stdin is allowed to be interpreted."
132*2a69962bSMickaël Salaünktap_test exec_stdin_reg 0 script-exec.inc
133*2a69962bSMickaël Salaün
134*2a69962bSMickaël Salaünktap_print_msg "By default, non-executable scripts are allowed to be interpreted, but not directly executed."
135*2a69962bSMickaël Salaün# We get 126 because of direct execution by Bash.
136*2a69962bSMickaël Salaünktap_test exec_direct 126 script-noexec.inc
137*2a69962bSMickaël Salaünktap_test exec_indirect 0 script-noexec.inc
138*2a69962bSMickaël Salaün
139*2a69962bSMickaël Salaünktap_print_msg "By default, non-executable stdin is allowed to be interpreted."
140*2a69962bSMickaël Salaünktap_test exec_stdin_reg 0 script-noexec.inc
141*2a69962bSMickaël Salaün
142*2a69962bSMickaël Salaünktap_print_msg "By default, interactive commands are allowed to be interpreted."
143*2a69962bSMickaël Salaünktap_test exec_interactive 0
144*2a69962bSMickaël Salaün
145*2a69962bSMickaël Salaün# With only file restriction: protect non-malicious users from inadvertent errors (e.g. python ~/Downloads/*.py).
146*2a69962bSMickaël Salaün
147*2a69962bSMickaël Salaünktap_print_msg "With -f, executable scripts are allowed to be interpreted and executed."
148*2a69962bSMickaël Salaünktap_test exec_direct 0 script-exec.inc ./set-exec -f --
149*2a69962bSMickaël Salaünktap_test exec_indirect 0 script-exec.inc ./set-exec -f --
150*2a69962bSMickaël Salaün
151*2a69962bSMickaël Salaünktap_print_msg "With -f, executable stdin is allowed to be interpreted."
152*2a69962bSMickaël Salaünktap_test exec_stdin_reg 0 script-exec.inc ./set-exec -f --
153*2a69962bSMickaël Salaün
154*2a69962bSMickaël Salaünktap_print_msg "With -f, non-executable scripts are not allowed to be executed nor interpreted."
155*2a69962bSMickaël Salaün# Direct execution of non-executable script is alwayse denied by the kernel.
156*2a69962bSMickaël Salaünktap_test exec_direct 1 script-noexec.inc ./set-exec -f --
157*2a69962bSMickaël Salaünktap_test exec_indirect 1 script-noexec.inc ./set-exec -f --
158*2a69962bSMickaël Salaün
159*2a69962bSMickaël Salaünktap_print_msg "With -f, non-executable stdin is allowed to be interpreted."
160*2a69962bSMickaël Salaünktap_test exec_stdin_reg 0 script-noexec.inc ./set-exec -f --
161*2a69962bSMickaël Salaün
162*2a69962bSMickaël Salaünktap_print_msg "With -f, interactive commands are allowed to be interpreted."
163*2a69962bSMickaël Salaünktap_test exec_interactive 0 ./set-exec -f --
164*2a69962bSMickaël Salaün
165*2a69962bSMickaël Salaün# With only denied interactive commands: check or monitor script content (e.g. with LSM).
166*2a69962bSMickaël Salaün
167*2a69962bSMickaël Salaünktap_print_msg "With -i, executable scripts are allowed to be interpreted and executed."
168*2a69962bSMickaël Salaünktap_test exec_direct 0 script-exec.inc ./set-exec -i --
169*2a69962bSMickaël Salaünktap_test exec_indirect 0 script-exec.inc ./set-exec -i --
170*2a69962bSMickaël Salaün
171*2a69962bSMickaël Salaünktap_print_msg "With -i, executable stdin is allowed to be interpreted."
172*2a69962bSMickaël Salaünktap_test exec_stdin_reg 0 script-exec.inc ./set-exec -i --
173*2a69962bSMickaël Salaün
174*2a69962bSMickaël Salaünktap_print_msg "With -i, non-executable scripts are allowed to be interpreted, but not directly executed."
175*2a69962bSMickaël Salaün# Direct execution of non-executable script is alwayse denied by the kernel.
176*2a69962bSMickaël Salaünktap_test exec_direct 1 script-noexec.inc ./set-exec -i --
177*2a69962bSMickaël Salaünktap_test exec_indirect 0 script-noexec.inc ./set-exec -i --
178*2a69962bSMickaël Salaün
179*2a69962bSMickaël Salaünktap_print_msg "With -i, non-executable stdin is not allowed to be interpreted."
180*2a69962bSMickaël Salaünktap_test exec_stdin_reg 1 script-noexec.inc ./set-exec -i --
181*2a69962bSMickaël Salaün
182*2a69962bSMickaël Salaünktap_print_msg "With -i, interactive commands are not allowed to be interpreted."
183*2a69962bSMickaël Salaünktap_test exec_interactive 1 ./set-exec -i --
184*2a69962bSMickaël Salaün
185*2a69962bSMickaël Salaün# With both file restriction and denied interactive commands: only allow executable scripts.
186*2a69962bSMickaël Salaün
187*2a69962bSMickaël Salaünktap_print_msg "With -fi, executable scripts are allowed to be interpreted and executed."
188*2a69962bSMickaël Salaünktap_test exec_direct 0 script-exec.inc ./set-exec -fi --
189*2a69962bSMickaël Salaünktap_test exec_indirect 0 script-exec.inc ./set-exec -fi --
190*2a69962bSMickaël Salaün
191*2a69962bSMickaël Salaünktap_print_msg "With -fi, executable stdin is allowed to be interpreted."
192*2a69962bSMickaël Salaünktap_test exec_stdin_reg 0 script-exec.inc ./set-exec -fi --
193*2a69962bSMickaël Salaün
194*2a69962bSMickaël Salaünktap_print_msg "With -fi, non-executable scripts are not allowed to be interpreted nor executed."
195*2a69962bSMickaël Salaün# Direct execution of non-executable script is alwayse denied by the kernel.
196*2a69962bSMickaël Salaünktap_test exec_direct 1 script-noexec.inc ./set-exec -fi --
197*2a69962bSMickaël Salaünktap_test exec_indirect 1 script-noexec.inc ./set-exec -fi --
198*2a69962bSMickaël Salaün
199*2a69962bSMickaël Salaünktap_print_msg "With -fi, non-executable stdin is not allowed to be interpreted."
200*2a69962bSMickaël Salaünktap_test exec_stdin_reg 1 script-noexec.inc ./set-exec -fi --
201*2a69962bSMickaël Salaün
202*2a69962bSMickaël Salaünktap_print_msg "With -fi, interactive commands are not allowed to be interpreted."
203*2a69962bSMickaël Salaünktap_test exec_interactive 1 ./set-exec -fi --
204*2a69962bSMickaël Salaün
205*2a69962bSMickaël Salaünktap_finished
206