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