summaryrefslogtreecommitdiffstats
path: root/debian/cryptdisks-functions
blob: ce5e6f4534336cc9c9e71c6c8614462e8080df73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#
# This file is for inclusion with
#    . /lib/cryptsetup/cryptdisks-functions
# and should not be executed directly.

PATH="/usr/sbin:/usr/bin:/sbin:/bin"
CRYPTDISKS_ENABLE="Yes"

#set -x

# Sanity check #1
[ -x /sbin/cryptsetup ] || exit 0

. /lib/lsb/init-functions
. /lib/cryptsetup/functions

if [ -r /etc/default/cryptdisks ]; then
    . /etc/default/cryptdisks
fi

MOUNT="$CRYPTDISKS_MOUNT"


# do_start()
#   Unlock all devices in the crypttab(5)
do_start() {
    [ -s "$TABFILE" ] || return 0

    # Create locking directory before invoking cryptsetup(8) to avoid warnings
    mkdir -pm0700 /run/cryptsetup
    modprobe -qb dm-mod || true
    modprobe -qb dm-crypt || true
    dmsetup mknodes >/dev/null 2>&1 || true

    if [ "$INITSTATE" != "init" ]; then
        log_action_begin_msg "Starting $INITSTATE crypto disks"
    fi
    mount_fs

    crypttab_foreach_entry _do_start_callback

    umount_fs
    log_action_end_msg 0
}
_do_start_callback() {
    setup_mapping || log_action_end_msg $?
}

# mount_fs()
#   Premounts file systems
mount_fs() {
    local point
    MOUNTED=""

    for point in $MOUNT; do
        if mount "$point" >/dev/null; then
            MOUNTED="$MOUNTED $point"
        fi
    done
}

# Postunmounts file systems
umount_fs() {
    local point

    for point in $MOUNTED; do
        umount "$point" >/dev/null
    done
}

# setup_mapping()
#   Set up a crypttab(5) mapping defined by $CRYPTTAB_NAME,
#   $CRYPTTAB_SOURCE, $CRYPTTAB_KEY, $CRYPTTAB_OPTIONS.
setup_mapping() {
    if dm_blkdevname "$CRYPTTAB_NAME" >/dev/null; then
        device_msg "running"
        return 0
    fi

    local loud="${DEFAULT_LOUD:-}"
    crypttab_parse_options --export --missing-path=fail || return 1
    if [ -n "${CRYPTTAB_OPTION_quiet+x}" ]; then
        loud="no"
    elif [ -n "${CRYPTTAB_OPTION_loud+x}" ]; then
        loud="yes"
    fi

    if [ -z "${FORCE_START-}" ]; then
        if [ "$INITSTATE" = "early" -a -n "${CRYPTTAB_OPTION_noearly+x}" ] ||
                [ "$INITSTATE" != "manual" -a -n "${CRYPTTAB_OPTION_noauto+x}" ]; then
            device_msg "ignored"
            return 0
        fi
    fi

    if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ] && [ "$CRYPTTAB_KEY" != "none" ]; then
        if ! crypttab_key_check; then
            device_msg "invalid key"
            return 1
        fi
        CRYPTTAB_OPTION_tries=1
    fi

    if ! crypttab_resolve_source; then
        if [ "$loud" = "yes" ]; then
            device_msg "skipped, device $CRYPTTAB_SOURCE does not exist"
        fi
        return 1
    fi
    device_msg "starting"

    local offset_bytes=""
    if [ -n "${CRYPTTAB_OPTION_offset+x}" ] && [ ${#CRYPTTAB_OPTION_offset} -le 7 ] && [ $CRYPTTAB_OPTION_offset -lt 4194304 ]; then
        # silently ignore large offset values which might cause the multiplication to overflow...
        offset_bytes=$((CRYPTTAB_OPTION_offset * 512))
    fi

    local out tmpdev
    if [ "$CRYPTTAB_TYPE" != "luks" ] && [ "$CRYPTTAB_TYPE" != "bitlk" ]; then
        # fail if the device has a filesystem and the disk encryption format doesn't
        # verify the key digest (unlike LUKS); unless it's swap, otherwise people can't
        # easily convert an existing plainttext swap partition to an encrypted one
        if ! out="$(/lib/cryptsetup/checks/un_blkid "$CRYPTTAB_SOURCE" "" ${CRYPTTAB_OPTION_offset+"$offset_bytes"} 2>/dev/null)" &&
                ! /lib/cryptsetup/checks/blkid "$CRYPTTAB_SOURCE" swap ${CRYPTTAB_OPTION_offset+"$offset_bytes"} >/dev/null; then
            log_warning_msg "$CRYPTTAB_NAME: the precheck for '$CRYPTTAB_SOURCE' failed: $out"
            return 1
        fi
    fi

    local count=0 maxtries="${CRYPTTAB_OPTION_tries:-3}" fstype rv
    local target="$CRYPTTAB_NAME"
    CRYPTTAB_NAME="${CRYPTTAB_NAME}_unformatted" # XXX potential conflict
    while [ $maxtries -le 0 ] || [ $count -lt $maxtries ]; do
        if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ] && [ "$CRYPTTAB_KEY" != "none" ]; then
            # unlock via keyfile
            unlock_mapping "$CRYPTTAB_KEY"
        else
            # unlock interactively or via keyscript
            CRYPTTAB_NAME="$target" run_keyscript "$count" | unlock_mapping
        fi
        rv=$?
        count=$(( $count + 1 ))

        if [ $rv -ne 0 ] || ! tmpdev="$(dm_blkdevname "$CRYPTTAB_NAME")"; then
            continue
        fi
        if [ -n "${CRYPTTAB_OPTION_check+x}" ] && \
                ! "$CRYPTTAB_OPTION_check" "$tmpdev" ${CRYPTTAB_OPTION_checkargs+"$CRYPTTAB_OPTION_checkargs"}; then
            log_warning_msg "$target: the check for '$CRYPTTAB_NAME' failed"
            cryptsetup remove -- "$CRYPTTAB_NAME"
            continue
        fi
        if [ "${CRYPTTAB_OPTION_swap+x}" ]; then
            if out="$(/lib/cryptsetup/checks/un_blkid "$tmpdev" "" ${CRYPTTAB_OPTION_offset+"$offset_bytes"} 2>/dev/null)" ||
                    /lib/cryptsetup/checks/blkid "$tmpdev" swap ${CRYPTTAB_OPTION_offset+"$offset_bytes"} >/dev/null 2>&1; then
                mkswap "$tmpdev" >/dev/null 2>&1
            else
                log_warning_msg "$target: the check for '$CRYPTTAB_NAME' failed. $CRYPTTAB_NAME contains data: $out"
                cryptsetup remove -- "$CRYPTTAB_NAME"
                return 1
            fi
        elif [ "${CRYPTTAB_OPTION_tmp+x}" ]; then
            local tmpdir="$(mktemp --tmpdir="/run/cryptsetup" --directory)" rv=0
            if ! mkfs -t "$CRYPTTAB_OPTION_tmp" -q "$tmpdev" >/dev/null 2>&1 ||
                    ! mount -t "$CRYPTTAB_OPTION_tmp" "$tmpdev" "$tmpdir" ||
                    ! chmod 1777 "$tmpdir"; then
                rv=1
            fi
            umount "$tmpdir" || true
            rmdir "$tmpdir" || true
            [ $rv -eq 0 ] || return $rv
        fi
        if command -v udevadm >/dev/null 2>&1; then
            udevadm settle
        fi
        dmsetup rename -- "$CRYPTTAB_NAME" "$target"
        device_msg "$target" "started"
        return 0
    done
    device_msg "$target" "failed"
    return 1
}

# Removes all mappings in crypttab, except the ones holding the root
# file system or /usr
do_stop() {
    local devno_rootfs devno_usr
    dmsetup mknodes
    log_action_begin_msg "Stopping $INITSTATE crypto disks"

    devno_rootfs="$(get_mnt_devno /)" || devno_rootfs=""
    devno_usr="$(get_mnt_devno /usr)" || devno_usr=""

    crypttab_foreach_entry _do_stop_callback
    log_action_end_msg 0
}
_do_stop_callback() {
    local skip="n" devno rv=0

    # traverse the device tree for each crypttab(5) entry and mark / and
    # /usr holders as skipped.  that's suboptimal but we can't use
    # mapped device names as they might contain any character other than
    # NUL.  shouldn't be much overhead anyway as the device tree is
    # likely not that long
    foreach_cryptdev _do_stop_skipped $devno_rootfs $devno_usr
    [ "$skip" = "n" ] || return $rv

    if devno="$(dmsetup info -c --noheadings -o devno -- "$CRYPTTAB_NAME" 2>/dev/null)" && [ -n "$devno" ]; then
        foreach_cryptdev --reverse _do_stop_remove "$devno" || rv=$? # try to remove slave devices first
    fi
    return $rv
}
_do_stop_skipped() {
    if [ "$1" = "$CRYPTTAB_NAME" ]; then
        skip="y"
    fi
}
_do_stop_remove() {
    local name="$1" i rv=0
    for i in 1 2 4 8 16 32; do
        remove_mapping "$name" 3<&- && break || rv=$?
        if [ $rv -eq 1 ] || [ $rv -eq 2 -a $i -gt 16 ]; then
            log_action_end_msg $rv
            break
        fi
        log_action_cont_msg "$name busy..."
        sleep $i
    done
}

# device_msg([$name], $message)
#   Convenience function to handle $VERBOSE
device_msg() {
    local name message
    if [ $# -eq 1 ]; then
        name="$CRYPTTAB_NAME"
        message="$1"
    else
        name="$1"
        message="$2"
    fi

    if [ "$VERBOSE" != "no" ]; then
        log_action_cont_msg "$name ($message)"
    fi
}

# remove_mapping($target)
#   Remove mapping $target
remove_mapping() {
    local CRYPTTAB_NAME="$1"

    if ! dm_blkdevname "$CRYPTTAB_NAME" >/dev/null; then
        device_msg "stopped"
        return 0
    fi

    if [ "$(dmsetup info --noheadings -c -o subsystem -- "$CRYPTTAB_NAME")" != "CRYPT" ]; then
        device_msg "error"
        return 1
    fi

    local opencount="$(dmsetup info -c --noheadings -o open -- "$CRYPTTAB_NAME" 2>/dev/null || true)"
    if [ -z "$opencount" ]; then
        device_msg "error"
        return 1
    elif [ "$opencount" != "0" ]; then
        device_msg "busy"
        if [ "$INITSTATE" = "early" ] || [ "$INITSTATE" = "manual" ]; then
            return 1
        elif [ "$INITSTATE" = "remaining" ]; then
            return 2
        fi
        return 0
    fi

    if cryptsetup remove -- "$CRYPTTAB_NAME"; then
        device_msg "stopping"
        return 0
    else
        device_msg "error"
        return 1
    fi
}

# vim: set filetype=sh :