ima_selinux.sh 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #!/bin/sh
  2. # SPDX-License-Identifier: GPL-2.0-or-later
  3. # Copyright (c) 2021 Microsoft Corporation
  4. # Author: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
  5. #
  6. # Verify measurement of SELinux policy hash and state.
  7. #
  8. # Relevant kernel commits:
  9. # * fdd1ffe8a812 ("selinux: include a consumer of the new IMA critical data hook")
  10. # * 2554a48f4437 ("selinux: measure state and policy capabilities")
  11. TST_NEEDS_CMDS="awk cut grep tail"
  12. TST_CNT=2
  13. TST_NEEDS_DEVICE=1
  14. TST_SETUP="setup"
  15. . ima_setup.sh
  16. FUNC_CRITICAL_DATA='func=CRITICAL_DATA'
  17. REQUIRED_POLICY="^measure.*$FUNC_CRITICAL_DATA"
  18. setup()
  19. {
  20. SELINUX_DIR=$(tst_get_selinux_dir)
  21. [ "$SELINUX_DIR" ] || tst_brk TCONF "SELinux is not enabled"
  22. require_ima_policy_content "$REQUIRED_POLICY" '-E' > $TST_TMPDIR/policy.txt
  23. }
  24. # Format of the measured SELinux state data.
  25. #
  26. # initialized=1;enforcing=0;checkreqprot=1;
  27. # network_peer_controls=1;open_perms=1;extended_socket_class=1;
  28. # always_check_network=0;cgroup_seclabel=1;nnp_nosuid_transition=1;
  29. # genfs_seclabel_symlinks=0;
  30. validate_policy_capabilities()
  31. {
  32. local measured_cap measured_value expected_value
  33. local inx=7
  34. # Policy capabilities flags start from "network_peer_controls"
  35. # in the measured SELinux state at offset 7 for 'awk'
  36. while [ $inx -lt 20 ]; do
  37. measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
  38. inx=$(($inx + 1))
  39. measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
  40. expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
  41. if [ "$measured_value" != "$expected_value" ]; then
  42. tst_res TFAIL "$measured_cap: expected: $expected_value, got: $digest"
  43. return
  44. fi
  45. inx=$(($inx + 1))
  46. done
  47. tst_res TPASS "SELinux state measured correctly"
  48. }
  49. # Trigger measurement of SELinux constructs and verify that
  50. # the measured SELinux policy hash matches the hash of the policy
  51. # loaded in kernel memory for SELinux.
  52. test1()
  53. {
  54. local policy_digest expected_policy_digest algorithm
  55. local data_source_name="selinux"
  56. local pattern="data_sources=[^[:space:]]*$data_source_name"
  57. local tmp_file="$TST_TMPDIR/selinux_policy_tmp_file.txt"
  58. tst_res TINFO "verifying SELinux policy hash measurement"
  59. # Trigger a measurement by changing SELinux state
  60. tst_update_selinux_state
  61. # Verify SELinux policy hash is measured and then validate that
  62. # the measured policy hash matches the hash of the policy currently
  63. # in kernel memory for SELinux
  64. line=$(grep -E "selinux-policy-hash" $ASCII_MEASUREMENTS | tail -1)
  65. if [ -z "$line" ]; then
  66. tst_res TFAIL "SELinux policy hash not measured"
  67. return
  68. fi
  69. algorithm=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f1)
  70. policy_digest=$(echo "$line" | cut -d' ' -f6)
  71. expected_policy_digest="$(compute_digest $algorithm $SELINUX_DIR/policy)" || \
  72. tst_brk TCONF "cannot compute digest for $algorithm"
  73. if [ "$policy_digest" != "$expected_policy_digest" ]; then
  74. tst_res TFAIL "Digest mismatch: expected: $expected_policy_digest, got: $policy_digest"
  75. return
  76. fi
  77. tst_res TPASS "SELinux policy hash measured correctly"
  78. }
  79. # Trigger measurement of SELinux constructs and verify that
  80. # the measured SELinux state matches the current SELinux
  81. # configuration.
  82. test2()
  83. {
  84. tst_check_cmds xxd || return
  85. local measured_data state_file="$TST_TMPDIR/selinux_state.txt"
  86. local data_source_name="selinux"
  87. local pattern="data_sources=[^[:space:]]*$data_source_name"
  88. local tmp_file="$TST_TMPDIR/selinux_state_tmp_file.txt"
  89. local digest expected_digest algorithm
  90. local initialized_value
  91. local enforced_value expected_enforced_value
  92. local checkreqprot_value expected_checkreqprot_value
  93. tst_res TINFO "verifying SELinux state measurement"
  94. # Trigger a measurement by changing SELinux state
  95. tst_update_selinux_state
  96. # Verify SELinux state is measured and then validate the measured
  97. # state matches that currently set for SELinux
  98. line=$(grep -E "selinux-state" $ASCII_MEASUREMENTS | tail -1)
  99. if [ -z "$line" ]; then
  100. tst_res TFAIL "SELinux state not measured"
  101. return
  102. fi
  103. digest=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f2)
  104. algorithm=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f1)
  105. echo "$line" | cut -d' ' -f6 | xxd -r -p > $state_file
  106. expected_digest="$(compute_digest $algorithm $state_file)" || \
  107. tst_brk TCONF "cannot compute digest for $algorithm"
  108. if [ "$digest" != "$expected_digest" ]; then
  109. tst_res TFAIL "digest mismatch: expected: $expected_digest, got: $digest"
  110. return
  111. fi
  112. # SELinux state is measured as the following string
  113. # initialized=1;enforcing=0;checkreqprot=1;
  114. # Value of 0 indicates the state is ON, and 1 indicates OFF
  115. #
  116. # enforce and checkreqprot measurement can be verified by
  117. # comparing the value of the file "enforce" and "checkreqprot"
  118. # respectively in the SELinux directory.
  119. # "initialized" is an internal state and should be set to 1
  120. # if enforce and checkreqprot are measured correctly.
  121. measured_data=$(cat $state_file)
  122. enforced_value=$(echo $measured_data | awk -F'[=;]' '{print $4}')
  123. expected_enforced_value=$(cat $SELINUX_DIR/enforce)
  124. if [ "$expected_enforced_value" != "$enforced_value" ]; then
  125. tst_res TFAIL "enforce: expected: $expected_enforced_value, got: $enforced_value"
  126. return
  127. fi
  128. checkreqprot_value=$(echo $measured_data | awk -F'[=;]' '{print $6}')
  129. expected_checkreqprot_value=$(cat $SELINUX_DIR/checkreqprot)
  130. if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ]; then
  131. tst_res TFAIL "checkreqprot: expected: $expected_checkreqprot_value, got: $checkreqprot_value"
  132. return
  133. fi
  134. initialized_value=$(echo $measured_data | awk -F'[=;]' '{print $2}')
  135. if [ "$initialized_value" != "1" ]; then
  136. tst_res TFAIL "initialized: expected 1, got: $initialized_value"
  137. return
  138. fi
  139. validate_policy_capabilities $measured_data
  140. }
  141. tst_run