clevis.in 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #!/bin/bash -e
  2. #
  3. # Copyright (c) 2017 Red Hat, Inc.
  4. # Copyright (c) 2017 Shawn Rose
  5. # Copyright (c) 2017 Guilhem Moulin
  6. #
  7. # Author: Harald Hoyer <harald@redhat.com>
  8. # Author: Nathaniel McCallum <npmccallum@redhat.com>
  9. # Author: Shawn Rose <shawnandrewrose@gmail.com>
  10. # Author: Guilhem Moulin <guilhem@guilhem.org>
  11. #
  12. # This program is free software: you can redistribute it and/or modify
  13. # it under the terms of the GNU General Public License as published by
  14. # the Free Software Foundation, either version 3 of the License, or
  15. # (at your option) any later version.
  16. #
  17. # This program is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. # You should have received a copy of the GNU General Public License
  23. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  24. #
  25. case $1 in
  26. prereqs) exit 0;;
  27. esac
  28. # Return 0 if $pid has a file descriptor pointing to $name, 1 otherwise
  29. in_fds() {
  30. local pid="$1" name="$2" fd
  31. for fd in /proc/$pid/fd/*; do
  32. if [ -e "$fd" ]; then
  33. [ "$(readlink -f "$fd")" != "$name" ] || return 0
  34. fi
  35. done
  36. return 1
  37. }
  38. # Print the PID of the askpass process with a file descriptor opened to
  39. # /lib/cryptsetup/passfifo if there is one.
  40. get_askpass_pid() {
  41. psinfo=$(ps) # Doing this so I don't end up matching myself
  42. echo "$psinfo" | awk "/$cryptkeyscript/ { print \$1 }" | while read -r pid; do
  43. if in_fds "$pid" "$PASSFIFO"; then
  44. echo "$pid"
  45. break
  46. fi
  47. done
  48. }
  49. luks1_decrypt() {
  50. luksmeta load "$@" \
  51. | clevis decrypt
  52. local rc
  53. for rc in "${PIPESTATUS[@]}"; do
  54. [ $rc -eq 0 ] || return $rc
  55. done
  56. return 0
  57. }
  58. luks2_jwe() {
  59. # jose jwe fmt -c outputs extra \n, so clean it up
  60. cryptsetup token export "$@" \
  61. | jose fmt -j- -Og jwe -o- \
  62. | jose jwe fmt -i- -c \
  63. | tr -d '\n'
  64. local rc
  65. for rc in "${PIPESTATUS[@]}"; do
  66. [ $rc -eq 0 ] || return $rc
  67. done
  68. return 0
  69. }
  70. # Wait for askpass, and then try and decrypt immediately. Just in case
  71. # there are multiple devices that need decrypting, this will loop
  72. # infinitely (The local-bottom script will kill this after decryption)
  73. clevisloop()
  74. {
  75. set -e
  76. # Set the path how we want it (Probably not all needed)
  77. PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin"
  78. if [ -x /bin/plymouth ] && plymouth --ping; then
  79. cryptkeyscript='plymouth ask-for-password'
  80. else
  81. # This has to be escaped for awk
  82. cryptkeyscript='\/lib\/cryptsetup\/askpass'
  83. fi
  84. PASSFIFO='/usr/lib/cryptsetup/passfifo'
  85. OLD_CRYPTTAB_SOURCE=""
  86. while true; do
  87. pid=$(get_askpass_pid)
  88. until [ "$pid" ] && [ -p "$PASSFIFO" ]; do
  89. sleep .1
  90. pid=$(get_askpass_pid)
  91. done
  92. # Import CRYPTTAB_SOURCE from the askpass process.
  93. local "$(grep '^CRYPTTAB_SOURCE=' /proc/"$pid"/environ)"
  94. # Make sure that CRYPTTAB_SOURCE is actually a block device
  95. [ ! -b "$CRYPTTAB_SOURCE" ] && continue
  96. # Make the source has changed if needed
  97. [ "$CRYPTTAB_SOURCE" = "$OLD_CRYPTTAB_SOURCE" ] && continue
  98. OLD_CRYPTTAB_SOURCE="$CRYPTTAB_SOURCE"
  99. UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
  100. if cryptsetup isLuks --type luks1 "$CRYPTTAB_SOURCE"; then
  101. # If the device is not initialized, sliently skip it.
  102. luksmeta test -d "$CRYPTTAB_SOURCE" || continue
  103. luksmeta show -d "$CRYPTTAB_SOURCE" | while read -r slot state uuid; do
  104. [ "$state" == "active" ] || continue
  105. [ "$uuid" == "$UUID" ] || continue
  106. if pt="$(luks1_decrypt -d "$CRYPTTAB_SOURCE" -s "$slot" -u "$UUID")"; then
  107. echo -n "$pt" > "$PASSFIFO"
  108. break
  109. fi
  110. done
  111. elif cryptsetup isLuks --type luks2 "$CRYPTTAB_SOURCE"; then
  112. cryptsetup luksDump "$CRYPTTAB_SOURCE" | sed -rn 's|^\s+([0-9]+): clevis|\1|p' | while read -r id; do
  113. jwe="$(luks2_jwe --token-id "$id" "$CRYPTTAB_SOURCE")" \
  114. || continue
  115. if pt="$(echo -n "$jwe" | clevis decrypt)"; then
  116. echo -n "$pt" > "$PASSFIFO"
  117. break
  118. fi
  119. done
  120. fi
  121. # Now that the current device has its password, let's sleep a
  122. # bit. This gives cryptsetup time to actually decrypt the
  123. # device and prompt for the next password if needed.
  124. sleep .5
  125. done
  126. }
  127. . /scripts/functions
  128. # This is a copy of 'all_netbootable_devices/all_non_enslaved_devices' for
  129. # platforms that might not provide it.
  130. clevis_all_netbootable_devices() {
  131. for device in /sys/class/net/* ; do
  132. if [ ! -e "$device/flags" ]; then
  133. continue
  134. fi
  135. loop=$(($(cat "$device/flags") & 0x8 && 1 || 0))
  136. bc=$(($(cat "$device/flags") & 0x2 && 1 || 0))
  137. ptp=$(($(cat "$device/flags") & 0x10 && 1 || 0))
  138. # Skip any device that is a loopback
  139. if [ $loop = 1 ]; then
  140. continue
  141. fi
  142. # Skip any device that isn't a broadcast
  143. # or point-to-point.
  144. if [ $bc = 0 ] && [ $ptp = 0 ]; then
  145. continue
  146. fi
  147. # Skip any enslaved device (has "master" link
  148. # attribute on it)
  149. device=$(basename "$device")
  150. ip -o link show "$device" | grep -q -w master && continue
  151. DEVICE="$DEVICE $device"
  152. done
  153. echo "$DEVICE"
  154. }
  155. # Check if network is up before trying to configure it.
  156. eth_check() {
  157. for device in $(clevis_all_netbootable_devices); do
  158. ip link set dev "$device" up
  159. sleep 1
  160. ETH_HAS_CARRIER=$(cat /sys/class/net/"$device"/carrier)
  161. if [ "$ETH_HAS_CARRIER" = '1' ]; then
  162. return 0
  163. fi
  164. done
  165. return 1
  166. }
  167. if eth_check; then
  168. # Make sure networking is set up: if booting via nfs, it already is
  169. # Doesn't seem to work when added to clevisloop for some reason
  170. [ "$boot" = nfs ] || configure_networking
  171. fi
  172. clevisloop &
  173. echo $! > /run/clevis.pid