| 1 | #!/sbin/openrc-run |
| 2 | # Copyright 1999-2015 Gentoo Foundation |
| 3 | # Distributed under the terms of the GNU General Public License v2 |
| 4 | |
| 5 | depend() { |
| 6 | before checkfs fsck |
| 7 | |
| 8 | if grep -qs ^swap= "${conf_file}" ; then |
| 9 | before swap |
| 10 | fi |
| 11 | } |
| 12 | |
| 13 | # We support multiple dmcrypt instances based on $SVCNAME |
| 14 | conf_file="/etc/conf.d/${SVCNAME}" |
| 15 | |
| 16 | # Get splash helpers if available. |
| 17 | if [ -e /sbin/splash-functions.sh ] ; then |
| 18 | . /sbin/splash-functions.sh |
| 19 | fi |
| 20 | |
| 21 | # Setup mappings for an individual target/swap |
| 22 | # Note: This relies on variables localized in the main body below. |
| 23 | dm_crypt_execute() { |
| 24 | local dev ret mode foo |
| 25 | |
| 26 | if [ -z "${target}" -a -z "${swap}" ] ; then |
| 27 | return |
| 28 | fi |
| 29 | |
| 30 | # Set up default values. |
| 31 | : ${dmcrypt_key_timeout:=1} |
| 32 | : ${dmcrypt_max_timeout:=300} |
| 33 | : ${dmcrypt_retries:=5} |
| 34 | |
| 35 | # Handle automatic look up of the source path. |
| 36 | if [ -z "${source}" -a -n "${loop_file}" ] ; then |
| 37 | source=$(losetup --show -f "${loop_file}") |
| 38 | fi |
| 39 | case ${source} in |
| 40 | *=*) |
| 41 | source="$(findfs ${source})" |
| 42 | ;; |
| 43 | esac |
| 44 | if [ -z "${source}" ] || [ ! -e "${source}" ] ; then |
| 45 | ewarn "source \"${source}\" for ${target} missing, skipping..." |
| 46 | return |
| 47 | fi |
| 48 | |
| 49 | if [ -n "${target}" ] ; then |
| 50 | # let user set options, otherwise leave empty |
| 51 | : ${options:=' '} |
| 52 | elif [ -n "${swap}" ] ; then |
| 53 | if cryptsetup isLuks ${source} 2>/dev/null ; then |
| 54 | ewarn "The swap you have defined is a LUKS partition. Aborting crypt-swap setup." |
| 55 | return |
| 56 | fi |
| 57 | target=${swap} |
| 58 | # swap contents do not need to be preserved between boots, luks not required. |
| 59 | # suspend2 users should have initramfs's init handling their swap partition either way. |
| 60 | : ${options:='-s 256 -c aes -d /dev/urandom'} |
| 61 | : ${pre_mount:='mkswap ${dev}'} |
| 62 | fi |
| 63 | |
| 64 | if [ -n "${loop_file}" ] ; then |
| 65 | dev="/dev/mapper/${target}" |
| 66 | ebegin " Setting up loop device ${source}" |
| 67 | losetup ${source} ${loop_file} |
| 68 | fi |
| 69 | |
| 70 | # cryptsetup: |
| 71 | # open <device> <name> # <device> is $source |
| 72 | # create <name> <device> # <name> is $target |
| 73 | local arg1="create" arg2="${target}" arg3="${source}" |
| 74 | if cryptsetup isLuks ${source} 2>/dev/null ; then |
| 75 | arg1="open" |
| 76 | arg2="${source}" |
| 77 | arg3="${target}" |
| 78 | fi |
| 79 | # This hack works around the lack of isLuks equivalents for alternative types |
| 80 | # Required feature request: https://gitlab.com/cryptsetup/cryptsetup/-/issues/824 |
| 81 | # tcrypt can't work this way since tcryptDump requires a password |
| 82 | if cryptsetup bitlkDump ${source} >/dev/null 2>&1 ; then |
| 83 | arg1="open --type bitlk" |
| 84 | arg2="${source}" |
| 85 | arg3="${target}" |
| 86 | fi |
| 87 | if cryptsetup fvault2Dump ${source} >/dev/null 2>&1 ; then |
| 88 | arg1="open --type fvault2" |
| 89 | arg2="${source}" |
| 90 | arg3="${target}" |
| 91 | fi |
| 92 | |
| 93 | # Older versions reported: |
| 94 | # ${target} is active: |
| 95 | # Newer versions report: |
| 96 | # ${target} is active[ and is in use.] |
| 97 | if cryptsetup status ${target} | grep -qE ' is active' ; then |
| 98 | einfo "dm-crypt mapping ${target} is already configured" |
| 99 | return |
| 100 | fi |
| 101 | splash svc_input_begin ${SVCNAME} >/dev/null 2>&1 |
| 102 | |
| 103 | # Handle keys |
| 104 | if [ -n "${key}" ] ; then |
| 105 | read_abort() { |
| 106 | # some colors |
| 107 | local ans savetty resettty |
| 108 | [ -z "${NORMAL}" ] && eval $(eval_ecolors) |
| 109 | einfon " $1? (${WARN}yes${NORMAL}/${GOOD}No${NORMAL}) " |
| 110 | shift |
| 111 | # This is ugly as s**t. But POSIX doesn't provide `read -t`, so |
| 112 | # we end up having to implement our own crap with stty/etc... |
| 113 | savetty=$(stty -g) |
| 114 | resettty='stty ${savetty}; trap - EXIT HUP INT TERM' |
| 115 | trap 'eval "${resettty}"' EXIT HUP INT TERM |
| 116 | stty -icanon |
| 117 | stty min 0 time "$(( $2 * 10 ))" |
| 118 | ans=$(dd count=1 bs=1 2>/dev/null) || ans='' |
| 119 | eval "${resettty}" |
| 120 | if [ -z "${ans}" ] ; then |
| 121 | printf '\r' |
| 122 | else |
| 123 | echo |
| 124 | fi |
| 125 | case ${ans} in |
| 126 | [yY]) return 0;; |
| 127 | *) return 1;; |
| 128 | esac |
| 129 | } |
| 130 | |
| 131 | # Notes: sed not used to avoid case where /usr partition is encrypted. |
| 132 | mode=${key##*:} && ( [ "${mode}" = "${key}" ] || [ -z "${mode}" ] ) && mode=reg |
| 133 | key=${key%:*} |
| 134 | case "${mode}" in |
| 135 | gpg|reg) |
| 136 | # handle key on removable device |
| 137 | if [ -n "${remdev}" ] ; then |
| 138 | # temp directory to mount removable device |
| 139 | local mntrem="${RC_SVCDIR}/dm-crypt-remdev.$$" |
| 140 | if [ ! -d "${mntrem}" ] ; then |
| 141 | if ! mkdir -p "${mntrem}" ; then |
| 142 | ewarn "${source} will not be decrypted ..." |
| 143 | einfo "Reason: Unable to create temporary mount point '${mntrem}'" |
| 144 | return |
| 145 | fi |
| 146 | fi |
| 147 | i=0 |
| 148 | einfo "Please insert removable device for ${target}" |
| 149 | while [ ${i} -lt ${dmcrypt_max_timeout} ] ; do |
| 150 | foo="" |
| 151 | if mount -n -o ro "${remdev}" "${mntrem}" 2>/dev/null >/dev/null ; then |
| 152 | # keyfile exists? |
| 153 | if [ ! -e "${mntrem}${key}" ] ; then |
| 154 | umount -n "${mntrem}" |
| 155 | rmdir "${mntrem}" |
| 156 | einfo "Cannot find ${key} on removable media." |
| 157 | read_abort "Abort" ${dmcrypt_key_timeout} && return |
| 158 | else |
| 159 | key="${mntrem}${key}" |
| 160 | break |
| 161 | fi |
| 162 | else |
| 163 | [ -e "${remdev}" ] \ |
| 164 | && foo="mount failed" \ |
| 165 | || foo="mount source not found" |
| 166 | fi |
| 167 | : $((i += 1)) |
| 168 | read_abort "Stop waiting after $i attempts (${foo})" -t 1 && return |
| 169 | done |
| 170 | else # keyfile ! on removable device |
| 171 | if [ ! -e "${key}" ] ; then |
| 172 | ewarn "${source} will not be decrypted ..." |
| 173 | einfo "Reason: keyfile ${key} does not exist." |
| 174 | return |
| 175 | fi |
| 176 | fi |
| 177 | ;; |
| 178 | *) |
| 179 | ewarn "${source} will not be decrypted ..." |
| 180 | einfo "Reason: mode ${mode} is invalid." |
| 181 | return |
| 182 | ;; |
| 183 | esac |
| 184 | else |
| 185 | mode=none |
| 186 | fi |
| 187 | ebegin " ${target} using: ${options} ${arg1} ${arg2} ${arg3}" |
| 188 | if [ "${mode}" = "gpg" ] ; then |
| 189 | : ${gpg_options:='-q -d'} |
| 190 | # gpg available ? |
| 191 | if command -v gpg >/dev/null ; then |
| 192 | i=0 |
| 193 | while [ ${i} -lt ${dmcrypt_retries} ] ; do |
| 194 | # paranoid, don't store key in a variable, pipe it so it stays very little in ram unprotected. |
| 195 | # save stdin stdout stderr "values" |
| 196 | timeout ${dmcrypt_max_timeout} gpg ${gpg_options} ${key} 2>/dev/null | \ |
| 197 | cryptsetup --key-file - ${options} ${arg1} ${arg2} ${arg3} |
| 198 | ret=$? |
| 199 | # The timeout command exits 124 when it times out. |
| 200 | [ ${ret} -eq 0 -o ${ret} -eq 124 ] && break |
| 201 | : $(( i += 1 )) |
| 202 | done |
| 203 | eend ${ret} "failure running cryptsetup" |
| 204 | else |
| 205 | ewarn "${source} will not be decrypted ..." |
| 206 | einfo "Reason: cannot find gpg application." |
| 207 | einfo "You have to install gnupg first." |
| 208 | einfo "If you have /usr on its own partition, try copying gpg to /bin ." |
| 209 | fi |
| 210 | else |
| 211 | if [ "${mode}" = "reg" ] ; then |
| 212 | cryptsetup ${options} -d ${key} ${arg1} ${arg2} ${arg3} |
| 213 | ret=$? |
| 214 | eend ${ret} "failure running cryptsetup" |
| 215 | else |
| 216 | cryptsetup ${options} ${arg1} ${arg2} ${arg3} |
| 217 | ret=$? |
| 218 | eend ${ret} "failure running cryptsetup" |
| 219 | fi |
| 220 | fi |
| 221 | if [ -d "${mntrem}" ] ; then |
| 222 | umount -n ${mntrem} 2>/dev/null >/dev/null |
| 223 | rmdir ${mntrem} 2>/dev/null >/dev/null |
| 224 | fi |
| 225 | splash svc_input_end ${SVCNAME} >/dev/null 2>&1 |
| 226 | |
| 227 | if [ ${ret} -ne 0 ] ; then |
| 228 | cryptfs_status=1 |
| 229 | else |
| 230 | if [ -n "${pre_mount}" ] ; then |
| 231 | dev="/dev/mapper/${target}" |
| 232 | eval ebegin \"" pre_mount: ${pre_mount}"\" |
| 233 | eval "${pre_mount}" > /dev/null |
| 234 | ewend $? || cryptfs_status=1 |
| 235 | fi |
| 236 | if [ -n "${post_mount}" ] ; then |
| 237 | dev="/dev/mapper/${target}" |
| 238 | eval ebegin \"" post_mount: ${post_mount}"\" |
| 239 | eval "${post_mount}" > /dev/null |
| 240 | ewend $? || cryptfs_status=1 |
| 241 | fi |
| 242 | fi |
| 243 | } |
| 244 | |
| 245 | # Lookup optional bootparams |
| 246 | get_bootparam_val() { |
| 247 | # We're given something like: |
| 248 | # foo=bar=cow |
| 249 | # Return the "bar=cow" part. |
| 250 | case $1 in |
| 251 | *=*) |
| 252 | echo "${1#*=}" |
| 253 | ;; |
| 254 | esac |
| 255 | } |
| 256 | |
| 257 | start() { |
| 258 | local header=true cryptfs_status=0 |
| 259 | local gpg_options key loop_file target targetline options pre_mount post_mount source swap remdev |
| 260 | |
| 261 | local x |
| 262 | for x in $(cat /proc/cmdline) ; do |
| 263 | case "${x}" in |
| 264 | key_timeout=*) |
| 265 | dmcrypt_key_timeout=$(get_bootparam_val "${x}") |
| 266 | ;; |
| 267 | esac |
| 268 | done |
| 269 | |
| 270 | while read targetline <&3 ; do |
| 271 | case ${targetline} in |
| 272 | # skip comments and blank lines |
| 273 | ""|"#"*) continue ;; |
| 274 | # skip service-specific openrc configs #377927 |
| 275 | rc_*) continue ;; |
| 276 | esac |
| 277 | |
| 278 | ${header} && ebegin "Setting up dm-crypt mappings" |
| 279 | header=false |
| 280 | |
| 281 | # check for the start of a new target/swap |
| 282 | case ${targetline} in |
| 283 | target=*|swap=*) |
| 284 | # If we have a target queued up, then execute it |
| 285 | dm_crypt_execute |
| 286 | |
| 287 | # Prepare for the next target/swap by resetting variables |
| 288 | unset gpg_options key loop_file target options pre_mount post_mount source swap remdev |
| 289 | ;; |
| 290 | |
| 291 | gpg_options=*|remdev=*|key=*|loop_file=*|options=*|pre_mount=*|post_mount=*|source=*) |
| 292 | if [ -z "${target}${swap}" ] ; then |
| 293 | ewarn "Ignoring setting outside target/swap section: ${targetline}" |
| 294 | continue |
| 295 | fi |
| 296 | ;; |
| 297 | |
| 298 | dmcrypt_*=*) |
| 299 | # ignore global options |
| 300 | continue |
| 301 | ;; |
| 302 | |
| 303 | *) |
| 304 | ewarn "Skipping invalid line in ${conf_file}: ${targetline}" |
| 305 | ;; |
| 306 | esac |
| 307 | |
| 308 | # Queue this setting for the next call to dm_crypt_execute |
| 309 | eval "${targetline}" |
| 310 | done 3< ${conf_file} |
| 311 | |
| 312 | # If we have a target queued up, then execute it |
| 313 | dm_crypt_execute |
| 314 | |
| 315 | ewend ${cryptfs_status} "Failed to setup dm-crypt devices" |
| 316 | } |
| 317 | |
| 318 | stop() { |
| 319 | local line header |
| 320 | |
| 321 | # Break down all mappings |
| 322 | header=true |
| 323 | grep -E "^(target|swap)=" ${conf_file} | \ |
| 324 | while read line ; do |
| 325 | ${header} && einfo "Removing dm-crypt mappings" |
| 326 | header=false |
| 327 | |
| 328 | target= swap= |
| 329 | eval ${line} |
| 330 | |
| 331 | [ -n "${swap}" ] && target=${swap} |
| 332 | if [ -z "${target}" ] ; then |
| 333 | ewarn "invalid line in ${conf_file}: ${line}" |
| 334 | continue |
| 335 | fi |
| 336 | |
| 337 | ebegin " ${target}" |
| 338 | cryptsetup remove ${target} |
| 339 | eend $? |
| 340 | done |
| 341 | |
| 342 | # Break down loop devices |
| 343 | header=true |
| 344 | grep '^source=./dev/loop' ${conf_file} | \ |
| 345 | while read line ; do |
| 346 | ${header} && einfo "Detaching dm-crypt loop devices" |
| 347 | header=false |
| 348 | |
| 349 | source= |
| 350 | eval ${line} |
| 351 | |
| 352 | ebegin " ${source}" |
| 353 | losetup -d "${source}" |
| 354 | eend $? |
| 355 | done |
| 356 | |
| 357 | return 0 |
| 358 | } |