clevis-luks-bind.in 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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 [-f] [-s SLT] [-k KEY] -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 " -s SLT The LUKS slot to use"
  40. echo
  41. echo " -k KEY Non-interactively read LUKS password from KEY file"
  42. echo " -k - Non-interactively read LUKS password from standard input"
  43. echo
  44. exit 2
  45. }
  46. if [ $# -eq 1 ] && [ "$1" == "--summary" ]; then
  47. echo "$SUMMARY"
  48. exit 0
  49. fi
  50. FRC=()
  51. while getopts ":hfd:s:k:" o; do
  52. case "$o" in
  53. f) FRC+=(-f);;
  54. d) DEV="$OPTARG";;
  55. s) SLT="$OPTARG";;
  56. k) KEY="$OPTARG";;
  57. *) usage;;
  58. esac
  59. done
  60. if [ -z "$DEV" ]; then
  61. echo "Did not specify a device!" >&2
  62. usage
  63. fi
  64. if ! cryptsetup isLuks "$DEV"; then
  65. echo "$DEV is not a LUKS device!" >&2
  66. exit 1
  67. fi
  68. if ! PIN="${@:$((OPTIND++)):1}" || [ -z "$PIN" ]; then
  69. echo "Did not specify a pin!" >&2
  70. usage
  71. fi
  72. if ! CFG="${@:$((OPTIND++)):1}" || [ -z "$CFG" ]; then
  73. echo "Did not specify a pin config!" >&2
  74. usage
  75. fi
  76. if luks2_supported; then
  77. if cryptsetup isLuks --type luks1 "$DEV"; then
  78. luks_type="luks1"
  79. elif cryptsetup isLuks --type luks2 "$DEV";then
  80. luks_type="luks2"
  81. else
  82. echo "$DEV is not a supported LUKS device!" >&2
  83. exit 1
  84. fi
  85. else
  86. luks_type="luks1"
  87. fi
  88. if [ "${luks_type}" = "luks1" ]; then
  89. # The first free slot, as per cryptsetup. In connection to bug #70, we may
  90. # have to wipe out the LUKSMeta slot priot to adding the new key.
  91. first_free_cs_slot=$(cryptsetup luksDump "${DEV}" \
  92. | sed -rn 's|^Key Slot ([0-7]): DISABLED$|\1|p' \
  93. | head -n 1)
  94. if [ -z "${first_free_cs_slot}" ]; then
  95. echo "There are no more free slots in ${DEV}!" >&2
  96. exit 1
  97. fi
  98. fi
  99. if [ -n "$KEY" ]; then
  100. if [ "$KEY" == "-" ]; then
  101. if [ "$luks_type" == "luks1" ]; then
  102. if ! luksmeta test -d "$DEV" && [ -z "${FRC[*]}" ]; then
  103. echo "Cannot use '-k-' without '-f' unless already initialized!" >&2
  104. usage
  105. fi
  106. fi
  107. elif ! [ -f "$KEY" ]; then
  108. echo "Key file '$KEY' not found!" >&2
  109. exit 1
  110. fi
  111. fi
  112. # Generate a key with the same entropy as the LUKS Master Key
  113. key="$(pwmake "$(
  114. cryptsetup luksDump "$DEV" \
  115. | if [ "$luks_type" == "luks1" ]; then
  116. sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p'
  117. else
  118. sed -rn 's|^\s+Key:\s+([0-9]+) bits\s*$|\1|p'
  119. fi | sort -n | tail -n 1
  120. )")"
  121. # Encrypt the new key
  122. jwe="$(echo -n "$key" | clevis encrypt "$PIN" "$CFG")"
  123. # If necessary, initialize the LUKS volume
  124. if [ "$luks_type" == "luks1" ] && ! luksmeta test -d "$DEV"; then
  125. luksmeta init -d "$DEV" "${FRC[@]}"
  126. fi
  127. # Get the existing key.
  128. case "$KEY" in
  129. "") read -r -s -p "Enter existing LUKS password: " existing_key; echo;;
  130. -) existing_key="$(/bin/cat)";;
  131. *) ! IFS= read -rd '' existing_key < "$KEY";;
  132. esac
  133. # Check if the key is valid.
  134. if ! cryptsetup luksOpen --test-passphrase "${DEV}" \
  135. --key-file <(echo -n "${existing_key}"); then
  136. exit 1
  137. fi
  138. if [ "$luks_type" == "luks1" ]; then
  139. # In certain circumstances, we may have LUKSMeta slots "not in sync" with
  140. # cryptsetup, which means we will try to save LUKSMeta metadata over an
  141. # already used or partially used slot -- github issue #70.
  142. # If that is the case, let's wipe the LUKSMeta slot here prior to saving.
  143. if read -r _ state uuid < <(luksmeta show -d "${DEV}" \
  144. | grep "^${first_free_cs_slot} *"); then
  145. if [ "${state}" = "inactive" ] && [ "${uuid}" = "${UUID}" ]; then
  146. luksmeta wipe -f -d "${DEV}" -s "${first_free_cs_slot}"
  147. fi
  148. fi
  149. fi
  150. # Add the new key.
  151. if [ -n "$SLT" ]; then
  152. cryptsetup luksAddKey --key-slot "$SLT" --key-file \
  153. <(echo -n "$existing_key") "$DEV"
  154. else
  155. if [ $luks_type == "luks2" ]; then
  156. readarray -t usedSlotsBeforeAddKey < <(cryptsetup luksDump "${DEV}" \
  157. | sed -rn 's|^\s+([0-9]+): luks2$|\1|p')
  158. else
  159. readarray -t usedSlotsBeforeAddKey < <(cryptsetup luksDump "${DEV}" \
  160. | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
  161. fi
  162. cryptsetup luksAddKey --key-file <(echo -n "${existing_key}") "$DEV"
  163. fi < <(echo -n "${key}")
  164. if [ $? -ne 0 ]; then
  165. echo "Error while adding new key to LUKS header!" >&2
  166. exit 1
  167. fi
  168. #Determine slot used by new key if a desired slot was not specified
  169. if [ -z "$SLT" ]; then
  170. if [ "$luks_type" == "luks2" ]; then
  171. readarray -t usedSlotsAfterAddKey < <(cryptsetup luksDump "${DEV}" \
  172. | sed -rn 's|^\s+([0-9]+): luks2$|\1|p')
  173. else
  174. readarray -t usedSlotsAfterAddKey < <(cryptsetup luksDump "${DEV}" \
  175. | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
  176. fi
  177. for i in "${usedSlotsAfterAddKey[@]}"; do
  178. if [[ ! " ${usedSlotsBeforeAddKey[@]} " =~ " ${i} " ]]; then
  179. SLT=$i
  180. break
  181. fi
  182. done
  183. fi
  184. if [ -z "$SLT" ]; then
  185. echo "Error while adding new key to LUKS header! Key slot is undefined." >&2
  186. exit 1
  187. fi
  188. if [ "$luks_type" == "luks1" ]; then
  189. echo -n "$jwe" | luksmeta save -d "$DEV" -u "$UUID" -s "$SLT" 2>/dev/null
  190. else
  191. printf '{"type":"clevis","keyslots":["%s"],"jwe":%s}' "$SLT" "$(jose jwe fmt -i- <<< "$jwe")" \
  192. | cryptsetup token import "$DEV"
  193. fi
  194. if [ $? -ne 0 ]; then
  195. echo "Error while saving Clevis metadata in LUKSMeta!" >&2
  196. echo -n "$key" | cryptsetup luksRemoveKey "$DEV"
  197. exit 1
  198. fi