clevis-luks-bind.in 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #!/bin/bash -e
  2. # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
  3. #
  4. # Copyright (c) 2016 Red Hat, Inc.
  5. # Author: Harald Hoyer <harald@redhat.com>
  6. # Author: Nathaniel McCallum <npmccallum@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. SUMMARY="Binds a LUKS device using the specified policy"
  22. UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
  23. # We require cryptsetup >= 2.0.4 to fully support LUKSv2.
  24. # Support is determined at build time.
  25. function luks2_supported() {
  26. return @OLD_CRYPTSETUP@
  27. }
  28. function usage() {
  29. exec >&2
  30. echo
  31. echo "Usage: clevis luks bind [-y] [-f] [-s SLT] [-k KEY] [-t TOKEN_ID] -d DEV PIN CFG"
  32. echo
  33. echo "$SUMMARY":
  34. echo
  35. echo " -f Do not prompt for LUKSMeta initialization"
  36. echo
  37. echo " -d DEV The LUKS device on which to perform binding"
  38. echo
  39. echo " -y Automatically answer yes for all questions"
  40. echo
  41. echo " -s SLT The LUKS slot to use"
  42. echo
  43. echo " -t TKN_ID The LUKS token ID to use; only available for LUKS2"
  44. echo
  45. echo " -k KEY Non-interactively read LUKS password from KEY file"
  46. echo " -k - Non-interactively read LUKS password from standard input"
  47. echo
  48. exit 2
  49. }
  50. if [ $# -eq 1 ] && [ "$1" == "--summary" ]; then
  51. echo "$SUMMARY"
  52. exit 0
  53. fi
  54. FRC=()
  55. YES=()
  56. while getopts ":hfyd:s:k:t:" o; do
  57. case "$o" in
  58. f) FRC+=(-f);;
  59. d) DEV="$OPTARG";;
  60. s) SLT="$OPTARG";;
  61. k) KEY="$OPTARG";;
  62. t) TOKEN_ID="$OPTARG";;
  63. y) FRC+=(-f)
  64. YES+=(-y);;
  65. *) usage;;
  66. esac
  67. done
  68. if [ -z "$DEV" ]; then
  69. echo "Did not specify a device!" >&2
  70. usage
  71. fi
  72. if ! cryptsetup isLuks "$DEV"; then
  73. echo "$DEV is not a LUKS device!" >&2
  74. exit 1
  75. fi
  76. if ! PIN="${@:$((OPTIND++)):1}" || [ -z "$PIN" ]; then
  77. echo "Did not specify a pin!" >&2
  78. usage
  79. elif ! EXE=$(command -v clevis-encrypt-"${PIN}") || [ -z "${EXE}" ]; then
  80. echo "'${PIN}' is not a valid pin!" >&2
  81. usage
  82. fi
  83. if ! CFG="${@:$((OPTIND++)):1}" || [ -z "$CFG" ]; then
  84. echo "Did not specify a pin config!" >&2
  85. usage
  86. fi
  87. if luks2_supported; then
  88. if cryptsetup isLuks --type luks1 "$DEV"; then
  89. luks_type="luks1"
  90. elif cryptsetup isLuks --type luks2 "$DEV";then
  91. luks_type="luks2"
  92. else
  93. echo "$DEV is not a supported LUKS device!" >&2
  94. exit 1
  95. fi
  96. else
  97. luks_type="luks1"
  98. fi
  99. if [ "$luks_type" = "luks1" -a -n "$TOKEN_ID" ]; then
  100. echo "$DEV is a LUKS1 device; -t is only supported in LUKS2"
  101. exit 1
  102. fi
  103. if [ "${luks_type}" = "luks1" ]; then
  104. # The first free slot, as per cryptsetup. In connection to bug #70, we may
  105. # have to wipe out the LUKSMeta slot priot to adding the new key.
  106. first_free_cs_slot=$(cryptsetup luksDump "${DEV}" \
  107. | sed -rn 's|^Key Slot ([0-7]): DISABLED$|\1|p' \
  108. | sed -n 1p)
  109. if [ -z "${first_free_cs_slot}" ]; then
  110. echo "There are no more free slots in ${DEV}!" >&2
  111. exit 1
  112. fi
  113. fi
  114. if [ -n "$KEY" ]; then
  115. if [ "$KEY" == "-" ]; then
  116. if [ "$luks_type" == "luks1" ]; then
  117. if ! luksmeta test -d "$DEV" && [ -z "${FRC[*]}" ]; then
  118. echo "Cannot use '-k-' without '-f' unless already initialized!" >&2
  119. usage
  120. fi
  121. fi
  122. elif ! [ -f "$KEY" ]; then
  123. echo "Key file '$KEY' not found!" >&2
  124. exit 1
  125. fi
  126. fi
  127. # Generate a key with the same entropy as the LUKS Master Key
  128. key="$(pwmake "$(
  129. cryptsetup luksDump "$DEV" \
  130. | if [ "$luks_type" == "luks1" ]; then
  131. sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p'
  132. else
  133. sed -rn 's|^\s+Key:\s+([0-9]+) bits\s*$|\1|p'
  134. fi | sort -n | tail -n 1
  135. )")"
  136. # Encrypt the new key
  137. jwe="$(echo -n "$key" | clevis encrypt "$PIN" "$CFG" "${YES}")"
  138. # If necessary, initialize the LUKS volume
  139. if [ "$luks_type" == "luks1" ] && ! luksmeta test -d "$DEV"; then
  140. luksmeta init -d "$DEV" "${FRC[@]}"
  141. fi
  142. # Get the existing key.
  143. case "$KEY" in
  144. "") read -r -s -p "Enter existing LUKS password: " existing_key; echo;;
  145. -) existing_key="$(/bin/cat)";;
  146. *) ! IFS= read -rd '' existing_key < "$KEY";;
  147. esac
  148. # Check if the key is valid.
  149. if ! cryptsetup luksOpen --test-passphrase "${DEV}" \
  150. --key-file <(echo -n "${existing_key}"); then
  151. exit 1
  152. fi
  153. if [ "$luks_type" == "luks1" ]; then
  154. # In certain circumstances, we may have LUKSMeta slots "not in sync" with
  155. # cryptsetup, which means we will try to save LUKSMeta metadata over an
  156. # already used or partially used slot -- github issue #70.
  157. # If that is the case, let's wipe the LUKSMeta slot here prior to saving.
  158. if read -r _ state uuid < <(luksmeta show -d "${DEV}" \
  159. | grep "^${first_free_cs_slot} *"); then
  160. if [ "${state}" = "inactive" ] && [ "${uuid}" = "${UUID}" ]; then
  161. luksmeta wipe -f -d "${DEV}" -s "${first_free_cs_slot}"
  162. fi
  163. fi
  164. fi
  165. # Add the new key.
  166. if [ -n "$SLT" ]; then
  167. cryptsetup luksAddKey --key-slot "$SLT" --key-file \
  168. <(echo -n "$existing_key") "$DEV"
  169. else
  170. if [ $luks_type == "luks2" ]; then
  171. readarray -t usedSlotsBeforeAddKey < <(cryptsetup luksDump "${DEV}" \
  172. | sed -rn 's|^\s+([0-9]+): luks2$|\1|p')
  173. else
  174. readarray -t usedSlotsBeforeAddKey < <(cryptsetup luksDump "${DEV}" \
  175. | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
  176. fi
  177. cryptsetup luksAddKey --key-file <(echo -n "${existing_key}") "$DEV"
  178. fi < <(echo -n "${key}")
  179. if [ $? -ne 0 ]; then
  180. echo "Error while adding new key to LUKS header!" >&2
  181. exit 1
  182. fi
  183. #Determine slot used by new key if a desired slot was not specified
  184. if [ -z "$SLT" ]; then
  185. if [ "$luks_type" == "luks2" ]; then
  186. readarray -t usedSlotsAfterAddKey < <(cryptsetup luksDump "${DEV}" \
  187. | sed -rn 's|^\s+([0-9]+): luks2$|\1|p')
  188. else
  189. readarray -t usedSlotsAfterAddKey < <(cryptsetup luksDump "${DEV}" \
  190. | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
  191. fi
  192. for i in "${usedSlotsAfterAddKey[@]}"; do
  193. if [[ ! " ${usedSlotsBeforeAddKey[@]} " =~ " ${i} " ]]; then
  194. SLT=$i
  195. break
  196. fi
  197. done
  198. fi
  199. if [ -z "$SLT" ]; then
  200. echo "Error while adding new key to LUKS header! Key slot is undefined." >&2
  201. exit 1
  202. fi
  203. if [ "$luks_type" == "luks1" ]; then
  204. echo -n "$jwe" | luksmeta save -d "$DEV" -u "$UUID" -s "$SLT" 2>/dev/null
  205. else
  206. printf '{"type":"clevis","keyslots":["%s"],"jwe":%s}' "$SLT" "$(jose jwe fmt -i- <<< "$jwe")" \
  207. | cryptsetup token import $([ -n "$TOKEN_ID" ] && echo '--token-id '$TOKEN_ID) "$DEV"
  208. fi
  209. if [ $? -ne 0 ]; then
  210. echo "Error while saving Clevis metadata in LUKSMeta!" >&2
  211. echo -n "$key" | cryptsetup luksRemoveKey "$DEV"
  212. exit 1
  213. fi