clevis-luks-report 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #!/bin/bash -e
  2. # vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
  3. #
  4. # Copyright (c) 2018, 2020 Red Hat, Inc.
  5. # Author: Radovan Sroka <rsroka@redhat.com>
  6. # Author: Sergio Correia <scorreia@redhat.com>
  7. #
  8. # This program is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. #
  21. . clevis-luks-common-functions
  22. SUMMARY="Report tang keys' rotations"
  23. if [ "${1}" = "--summary" ]; then
  24. echo "${SUMMARY}"
  25. exit 0
  26. fi
  27. report_compare() {
  28. local adv_keys="${1}"
  29. local mdata_keys="${2}"
  30. [ -z "${adv_keys}" ] && return 1
  31. [ -z "${mdata_keys}" ] && return 1
  32. local thp keys
  33. for thp in $(printf '%s' "${mdata_keys}" | jose jwk thp --input=-); do
  34. if ! printf '%s' "${adv_keys}" | jose jwk thp --input=- \
  35. --find "${thp}" >/dev/null; then
  36. keys="$(printf '%s %s' "${keys}" "${thp}")"
  37. fi
  38. done
  39. printf '%s' "${keys}"
  40. }
  41. report_tang() {
  42. local content="${1}"
  43. [ -z "${content}" ] && return 1
  44. local url
  45. if ! url="$(jose fmt --json="${content}" --get url --unquote=-)" \
  46. || [ -z "${url}" ]; then
  47. echo "Invalid tang metadata; URL not found" >&2
  48. return 1
  49. fi
  50. local jws
  51. if ! jws="$(curl -sfg "${url}/adv")"; then
  52. echo "Unable to fetch advertisement (${url}/adv)" >&2
  53. return 1
  54. fi
  55. local adv_keys
  56. if ! adv_keys="$(jose fmt --json="${jws}" --object --get payload \
  57. --string --b64load --object --get keys \
  58. --array --unwind --output=-)"; then
  59. echo "Advertisement is malformed" >&2
  60. return 1
  61. fi
  62. # Check advertisement validity.
  63. local ver
  64. if ! ver="$(printf '%s' "${adv_keys}" | jose jwk use --input=- \
  65. --required \
  66. --use=verify \
  67. --output=-)"; then
  68. echo "Unable to validate advertisement" >&2
  69. return 1
  70. fi
  71. if ! printf '%s' "${ver}" | jose jws ver --input="${jws}" --key=- \
  72. --all; then
  73. echo "Advertisement is missing signatures" >&2
  74. return 1
  75. fi
  76. local mdata_keys
  77. if ! mdata_keys="$(jose fmt --json="${content}" --get adv --output=-)" \
  78. || [ -z "${mdata_keys}" ]; then
  79. echo "Keys from clevis metadata not found" >&2
  80. return 1
  81. fi
  82. report_compare "${adv_keys}" "${mdata_keys}"
  83. }
  84. report_sss() {
  85. local content="${1}"
  86. [ -z "${content}" ] && return 1
  87. local jwe
  88. for jwe in $(jose fmt --json="${content}" --get jwe --foreach=-); do
  89. jwe="$(printf '%s' "${jwe}" | sed -e 's/"//g')"
  90. report_decode "${jwe}"
  91. done
  92. }
  93. report_decode() {
  94. local data64="${1}"
  95. [ -z "${data64}" ] && return 1
  96. local data
  97. if ! data="$(clevis_luks_decode_jwe "${data64}")" || [ -z "${data}" ]; then
  98. echo "Unable to decode metadata" >&2
  99. exit 1
  100. fi
  101. local pin
  102. if ! pin="$(jose fmt --json="${data}" --get clevis --get pin --unquote=-)" \
  103. || [ -z "${pin}" ]; then
  104. echo "Pin not found in clevis metadata" >&2
  105. exit 1
  106. fi
  107. local content
  108. if ! content="$(jose fmt --json="${data}" --get clevis --get "${pin}" \
  109. --output=-)" || [ -z "${content}" ]; then
  110. echo "Invalid pin metadata; no content found" >&2
  111. return 1
  112. fi
  113. case "${pin}" in
  114. tang)
  115. report_tang "${content}"
  116. ;;
  117. sss)
  118. report_sss "${content}"
  119. ;;
  120. esac
  121. }
  122. usage_and_exit () {
  123. exec >&2
  124. echo "Usage: clevis luks report [-q] [-r] -d DEV -s SLOT"
  125. echo
  126. echo "${SUMMARY}"
  127. echo
  128. echo " -d DEV The LUKS device to check for key rotations"
  129. echo
  130. echo " -s SLT The LUKS slot to use"
  131. echo
  132. echo " -q Quiet mode; do not prompt for using 'clevis luks regen'"
  133. echo
  134. echo " -r Regenerate binding with 'clevis luks regen -q -d DEV -s SLOT'"
  135. echo
  136. exit "${1}"
  137. }
  138. while getopts "hd:s:rq" o; do
  139. case "${o}" in
  140. d) DEV="${OPTARG}";;
  141. h) usage_and_exit 0;;
  142. r) ROPT="regen";;
  143. s) SLT="${OPTARG}";;
  144. q) QOPT="quiet";;
  145. *) usage_and_exit 1;;
  146. esac
  147. done
  148. if [ -z "${DEV}" ]; then
  149. echo "Did not specify a device!" >&2
  150. exit 1
  151. fi
  152. if [ -z "${SLT}" ]; then
  153. echo "Did not specify a slot!" >&2
  154. exit 1
  155. fi
  156. if ! data64="$(clevis_luks_read_slot "${DEV}" "${SLT}")" \
  157. || [ -z "${data64}" ]; then
  158. # Error message was already displayed by clevis_luks_read_slot(),
  159. # at this point.
  160. exit 1
  161. fi
  162. if ! keys="$(report_decode "${data64}")"; then
  163. echo "Unable to verify whether there are rotated keys" >&2
  164. exit 1
  165. fi
  166. # No rotated keys.
  167. [ -z "${keys}" ] && exit 0
  168. echo "The following keys are not in the current advertisement and were probably rotated:"
  169. for k in ${keys}; do
  170. printf ' %s\n' "${k}"
  171. done
  172. if [ -z "${QOPT}" ] && [ -z "${ROPT}" ]; then
  173. read -r -p "Do you want to regenerate the binding with \"clevis luks regen -q -d ${DEV} -s ${SLT}\"? [ynYN] " ans
  174. if [ "${ans}" = "y" ] || [ "${ans}" = "Y" ]; then
  175. ROPT="regen"
  176. fi
  177. fi
  178. if [ "${ROPT}" = "regen" ]; then
  179. if ! EXE="$(command -v clevis-luks-regen)" || [ -z "${EXE}" ]; then
  180. echo "Unable to find clevis luks regen" >&2
  181. exit 1
  182. fi
  183. exec "${EXE}" -q -d "${DEV}" -s "${SLT}"
  184. fi
  185. exit 1