check-exec-tests.sh 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #!/usr/bin/env bash
  2. # SPDX-License-Identifier: GPL-2.0
  3. #
  4. # Test the "inc" interpreter.
  5. #
  6. # See include/uapi/linux/securebits.h, include/uapi/linux/fcntl.h and
  7. # samples/check-exec/inc.c
  8. #
  9. # Copyright © 2024 Microsoft Corporation
  10. set -u -e -o pipefail
  11. EXPECTED_OUTPUT="1"
  12. exec 2>/dev/null
  13. DIR="$(dirname $(readlink -f "$0"))"
  14. source "${DIR}"/../kselftest/ktap_helpers.sh
  15. exec_direct() {
  16. local expect="$1"
  17. local script="$2"
  18. shift 2
  19. local ret=0
  20. local out
  21. # Updates PATH for `env` to execute the `inc` interpreter.
  22. out="$(PATH="." "$@" "${script}")" || ret=$?
  23. if [[ ${ret} -ne ${expect} ]]; then
  24. echo "ERROR: Wrong expectation for direct file execution: ${ret}"
  25. return 1
  26. fi
  27. if [[ ${ret} -eq 0 && "${out}" != "${EXPECTED_OUTPUT}" ]]; then
  28. echo "ERROR: Wrong output for direct file execution: ${out}"
  29. return 1
  30. fi
  31. }
  32. exec_indirect() {
  33. local expect="$1"
  34. local script="$2"
  35. shift 2
  36. local ret=0
  37. local out
  38. # Script passed as argument.
  39. out="$("$@" ./inc "${script}")" || ret=$?
  40. if [[ ${ret} -ne ${expect} ]]; then
  41. echo "ERROR: Wrong expectation for indirect file execution: ${ret}"
  42. return 1
  43. fi
  44. if [[ ${ret} -eq 0 && "${out}" != "${EXPECTED_OUTPUT}" ]]; then
  45. echo "ERROR: Wrong output for indirect file execution: ${out}"
  46. return 1
  47. fi
  48. }
  49. exec_stdin_reg() {
  50. local expect="$1"
  51. local script="$2"
  52. shift 2
  53. local ret=0
  54. local out
  55. # Executing stdin must be allowed if the related file is executable.
  56. out="$("$@" ./inc -i < "${script}")" || ret=$?
  57. if [[ ${ret} -ne ${expect} ]]; then
  58. echo "ERROR: Wrong expectation for stdin regular file execution: ${ret}"
  59. return 1
  60. fi
  61. if [[ ${ret} -eq 0 && "${out}" != "${EXPECTED_OUTPUT}" ]]; then
  62. echo "ERROR: Wrong output for stdin regular file execution: ${out}"
  63. return 1
  64. fi
  65. }
  66. exec_stdin_pipe() {
  67. local expect="$1"
  68. shift
  69. local ret=0
  70. local out
  71. # A pipe is not executable.
  72. out="$(cat script-exec.inc | "$@" ./inc -i)" || ret=$?
  73. if [[ ${ret} -ne ${expect} ]]; then
  74. echo "ERROR: Wrong expectation for stdin pipe execution: ${ret}"
  75. return 1
  76. fi
  77. }
  78. exec_argument() {
  79. local expect="$1"
  80. local ret=0
  81. shift
  82. local out
  83. # Script not coming from a file must not be executed.
  84. out="$("$@" ./inc -c "$(< script-exec.inc)")" || ret=$?
  85. if [[ ${ret} -ne ${expect} ]]; then
  86. echo "ERROR: Wrong expectation for arbitrary argument execution: ${ret}"
  87. return 1
  88. fi
  89. if [[ ${ret} -eq 0 && "${out}" != "${EXPECTED_OUTPUT}" ]]; then
  90. echo "ERROR: Wrong output for arbitrary argument execution: ${out}"
  91. return 1
  92. fi
  93. }
  94. exec_interactive() {
  95. exec_stdin_pipe "$@"
  96. exec_argument "$@"
  97. }
  98. ktap_test() {
  99. ktap_test_result "$*" "$@"
  100. }
  101. ktap_print_header
  102. ktap_set_plan 28
  103. # Without secbit configuration, nothing is changed.
  104. ktap_print_msg "By default, executable scripts are allowed to be interpreted and executed."
  105. ktap_test exec_direct 0 script-exec.inc
  106. ktap_test exec_indirect 0 script-exec.inc
  107. ktap_print_msg "By default, executable stdin is allowed to be interpreted."
  108. ktap_test exec_stdin_reg 0 script-exec.inc
  109. ktap_print_msg "By default, non-executable scripts are allowed to be interpreted, but not directly executed."
  110. # We get 126 because of direct execution by Bash.
  111. ktap_test exec_direct 126 script-noexec.inc
  112. ktap_test exec_indirect 0 script-noexec.inc
  113. ktap_print_msg "By default, non-executable stdin is allowed to be interpreted."
  114. ktap_test exec_stdin_reg 0 script-noexec.inc
  115. ktap_print_msg "By default, interactive commands are allowed to be interpreted."
  116. ktap_test exec_interactive 0
  117. # With only file restriction: protect non-malicious users from inadvertent errors (e.g. python ~/Downloads/*.py).
  118. ktap_print_msg "With -f, executable scripts are allowed to be interpreted and executed."
  119. ktap_test exec_direct 0 script-exec.inc ./set-exec -f --
  120. ktap_test exec_indirect 0 script-exec.inc ./set-exec -f --
  121. ktap_print_msg "With -f, executable stdin is allowed to be interpreted."
  122. ktap_test exec_stdin_reg 0 script-exec.inc ./set-exec -f --
  123. ktap_print_msg "With -f, non-executable scripts are not allowed to be executed nor interpreted."
  124. # Direct execution of non-executable script is alwayse denied by the kernel.
  125. ktap_test exec_direct 1 script-noexec.inc ./set-exec -f --
  126. ktap_test exec_indirect 1 script-noexec.inc ./set-exec -f --
  127. ktap_print_msg "With -f, non-executable stdin is allowed to be interpreted."
  128. ktap_test exec_stdin_reg 0 script-noexec.inc ./set-exec -f --
  129. ktap_print_msg "With -f, interactive commands are allowed to be interpreted."
  130. ktap_test exec_interactive 0 ./set-exec -f --
  131. # With only denied interactive commands: check or monitor script content (e.g. with LSM).
  132. ktap_print_msg "With -i, executable scripts are allowed to be interpreted and executed."
  133. ktap_test exec_direct 0 script-exec.inc ./set-exec -i --
  134. ktap_test exec_indirect 0 script-exec.inc ./set-exec -i --
  135. ktap_print_msg "With -i, executable stdin is allowed to be interpreted."
  136. ktap_test exec_stdin_reg 0 script-exec.inc ./set-exec -i --
  137. ktap_print_msg "With -i, non-executable scripts are allowed to be interpreted, but not directly executed."
  138. # Direct execution of non-executable script is alwayse denied by the kernel.
  139. ktap_test exec_direct 1 script-noexec.inc ./set-exec -i --
  140. ktap_test exec_indirect 0 script-noexec.inc ./set-exec -i --
  141. ktap_print_msg "With -i, non-executable stdin is not allowed to be interpreted."
  142. ktap_test exec_stdin_reg 1 script-noexec.inc ./set-exec -i --
  143. ktap_print_msg "With -i, interactive commands are not allowed to be interpreted."
  144. ktap_test exec_interactive 1 ./set-exec -i --
  145. # With both file restriction and denied interactive commands: only allow executable scripts.
  146. ktap_print_msg "With -fi, executable scripts are allowed to be interpreted and executed."
  147. ktap_test exec_direct 0 script-exec.inc ./set-exec -fi --
  148. ktap_test exec_indirect 0 script-exec.inc ./set-exec -fi --
  149. ktap_print_msg "With -fi, executable stdin is allowed to be interpreted."
  150. ktap_test exec_stdin_reg 0 script-exec.inc ./set-exec -fi --
  151. ktap_print_msg "With -fi, non-executable scripts are not allowed to be interpreted nor executed."
  152. # Direct execution of non-executable script is alwayse denied by the kernel.
  153. ktap_test exec_direct 1 script-noexec.inc ./set-exec -fi --
  154. ktap_test exec_indirect 1 script-noexec.inc ./set-exec -fi --
  155. ktap_print_msg "With -fi, non-executable stdin is not allowed to be interpreted."
  156. ktap_test exec_stdin_reg 1 script-noexec.inc ./set-exec -fi --
  157. ktap_print_msg "With -fi, interactive commands are not allowed to be interpreted."
  158. ktap_test exec_interactive 1 ./set-exec -fi --
  159. ktap_finished