File: fips.sh

package info (click to toggle)
dracut 109-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,872 kB
  • sloc: sh: 24,527; ansic: 5,234; makefile: 346; perl: 186; python: 48; javascript: 19
file content (246 lines) | stat: -rwxr-xr-x 8,548 bytes parent folder | download | duplicates (3)
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
#!/bin/sh

command -v getarg > /dev/null || . /lib/dracut-lib.sh

# systemd lets stdout go to journal only, but the system
# has to halt when the integrity check fails to satisfy FIPS.
if [ -z "${DRACUT_SYSTEMD-}" ]; then
    fips_info() {
        info "$*"
    }
else
    fips_info() {
        echo "$*" >&2
    }
fi

# Checks if a systemd-based UKI is running and ESP UUID is set
is_uki() {
    [ -f /sys/firmware/efi/efivars/StubFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ] \
        && [ -f /sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ]
}

mount_boot() {
    boot=$(getarg boot=)

    if is_uki && [ -z "$boot" ]; then
        # efivar file has 4 bytes header and contain UCS-2 data. Note, 'cat' is required
        # as sys/firmware/efi/efivars/ files are 'special' and don't allow 'seeking'.
        # shellcheck disable=SC2002
        boot="PARTUUID=$(cat /sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f | tail -c +5 | tr -d '\0' | tr 'A-F' 'a-f')"
    fi

    if [ -n "$boot" ]; then
        if [ -d /boot ] && ismounted /boot; then
            boot_dev=
            if command -v findmnt > /dev/null; then
                boot_dev=$(findmnt -n -o SOURCE /boot)
            fi
            fips_info "Ignoring 'boot=$boot' as /boot is already mounted ${boot_dev:+"from '$boot_dev'"}"
            return 0
        fi

        case "$boot" in
            LABEL=* | UUID=* | PARTUUID=* | PARTLABEL=*)
                boot="$(label_uuid_to_dev "$boot")"
                ;;
            /dev/*) ;;

            *)
                die "You have to specify boot=<boot device> as a boot option for fips=1"
                ;;
        esac

        if ! [ -e "$boot" ]; then
            udevadm trigger --action=add > /dev/null 2>&1

            i=0
            while ! [ -e "$boot" ]; do
                udevadm settle --exit-if-exists="$boot"
                [ -e "$boot" ] && break
                sleep 0.5
                i=$((i + 1))
                [ $i -gt 40 ] && break
            done
        fi

        [ -e "$boot" ] || return 1

        mkdir -p /boot
        fips_info "Mounting $boot as /boot"
        mount -oro "$boot" /boot || return 1
        FIPS_MOUNTED_BOOT=1
    elif ! ismounted /boot && [ -d "$NEWROOT/boot" ]; then
        # shellcheck disable=SC2114
        rm -fr -- /boot
        ln -sf "$NEWROOT/boot" /boot
    else
        die "You have to specify boot=<boot device> as a boot option for fips=1"
    fi
}

do_rhevh_check() {
    KERNEL=$(uname -r)
    kpath=${1}

    # If we're on RHEV-H, the kernel is in /run/initramfs/live/vmlinuz0
    HMAC_SUM_ORIG=$(while read -r a _ || [ -n "$a" ]; do printf "%s\n" "$a"; done < "$NEWROOT/boot/.vmlinuz-${KERNEL}.hmac")
    HMAC_SUM_CALC=$(sha512hmac "$kpath" | while read -r a _ || [ -n "$a" ]; do printf "%s\n" "$a"; done || return 1)
    if [ -z "$HMAC_SUM_ORIG" ] || [ -z "$HMAC_SUM_CALC" ] || [ "${HMAC_SUM_ORIG}" != "${HMAC_SUM_CALC}" ]; then
        warn "HMAC sum mismatch"
        return 1
    fi
    fips_info "rhevh_check OK"
    return 0
}

do_uki_check() {
    local KVER
    local uki_checked=0

    KVER="$(uname -r)"
    # UKI are placed in $ESP\EFI\Linux\<intall-tag>-<uname-r>.efi
    if ! [ "$FIPS_MOUNTED_BOOT" = 1 ]; then
        warn "Failed to mount ESP for doing UKI integrity check"
        return 1
    fi

    for UKIpath in /boot/EFI/Linux/*-"$KVER".efi; do
        # UKIs are installed to $ESP/EFI/Linux/<entry-token-or-machine-id>-<uname-r>.efi
        # and in some cases (e.g. when the image is used as a template for creating new
        # VMs) entry-token-or-machine-id can change. To make sure the running UKI is
        # always checked, check all UKIs which match the 'uname -r' of the running kernel
        # and fail the whole check if any of the matching UKIs are corrupted.

        [ -r "$UKIpath" ] || break

        local UKI="${UKIpath##*/}"
        local UKIHMAC=."$UKI".hmac

        fips_info "checking $UKIHMAC"
        (cd /boot/EFI/Linux/ && sha512hmac -c "$UKIHMAC") || return 1
        uki_checked=1
    done

    if [ "$uki_checked" = 0 ]; then
        warn "Failed for find UKI for checking"
        return 1
    fi
    return 0
}

nonfatal_modprobe() {
    modprobe "$1" 2>&1 > /dev/stdout \
        | while read -r line || [ -n "$line" ]; do
            echo "${line#modprobe: FATAL: }" >&2
        done
}

fips_load_crypto() {
    local _k
    local _v
    local _module
    local _found

    fips_info "Loading and integrity checking all crypto modules"
    while read -r _module; do
        if [ "$_module" != "tcrypt" ]; then
            if ! nonfatal_modprobe "${_module}" 2> /tmp/fips.modprobe_err; then
                # check if kernel provides generic algo
                _found=0
                while read -r _k _ _v || [ -n "$_k" ]; do
                    [ "$_k" != "name" ] && [ "$_k" != "driver" ] && continue
                    [ "$_v" != "$_module" ] && continue
                    _found=1
                    break
                done < /proc/crypto
                [ "$_found" = "0" ] && cat /tmp/fips.modprobe_err >&2 && return 1
            fi
        fi
    done < /etc/fipsmodules
    if [ -f /etc/fips.conf ]; then
        mkdir -p /run/modprobe.d
        cp /etc/fips.conf /run/modprobe.d/fips.conf
    fi

    fips_info "Self testing crypto algorithms"
    modprobe tcrypt || return 1
    rmmod tcrypt
}

do_fips() {
    KERNEL=$(uname -r)

    if ! getarg rd.fips.skipkernel > /dev/null; then

        fips_info "Checking integrity of kernel"
        if [ -e "/run/initramfs/live/vmlinuz0" ]; then
            do_rhevh_check /run/initramfs/live/vmlinuz0 || return 1
        elif [ -e "/run/initramfs/live/isolinux/vmlinuz0" ]; then
            do_rhevh_check /run/initramfs/live/isolinux/vmlinuz0 || return 1
        elif [ -e "/run/install/repo/images/pxeboot/vmlinuz" ]; then
            # This is a boot.iso with the .hmac inside the install.img
            do_rhevh_check /run/install/repo/images/pxeboot/vmlinuz || return 1
        elif is_uki; then
            # This is a UKI
            do_uki_check || return 1
        else
            BOOT_IMAGE="$(getarg BOOT_IMAGE)"

            # On s390x, BOOT_IMAGE isn't a path but an integer representing the
            # entry number selected. Let's try the root of /boot first, and
            # otherwise fallback to trying to parse the BLS entries if it's a
            # BLS-based system.
            if [ "$(uname -m)" = s390x ]; then
                if [ -e "/boot/vmlinuz-${KERNEL}" ]; then
                    BOOT_IMAGE="vmlinuz-${KERNEL}"
                elif [ -d /boot/loader/entries ]; then
                    bls=$(find /boot/loader/entries -name '*.conf' | sort -rV | sed -n "$((BOOT_IMAGE + 1))p")
                    if [ -e "${bls}" ]; then
                        BOOT_IMAGE=$(grep ^linux "${bls}" | cut -d' ' -f2)
                    fi
                fi
            fi

            # Trim off any leading GRUB boot device (e.g. ($root) )
            BOOT_IMAGE="$(echo "${BOOT_IMAGE}" | sed 's/^(.*)//')"

            BOOT_IMAGE_NAME="${BOOT_IMAGE##*/}"
            BOOT_IMAGE_PATH="${BOOT_IMAGE%"${BOOT_IMAGE_NAME}"}"

            if [ -z "$BOOT_IMAGE_NAME" ]; then
                BOOT_IMAGE_NAME="vmlinuz-${KERNEL}"
            elif ! [ -e "/boot/${BOOT_IMAGE_PATH}/${BOOT_IMAGE_NAME}" ]; then
                #if /boot is not a separate partition BOOT_IMAGE might start with /boot
                BOOT_IMAGE_PATH=${BOOT_IMAGE_PATH#"/boot"}
                #on some architectures BOOT_IMAGE does not contain path to kernel
                #so if we can't find anything, let's treat it in the same way as if it was empty
                if ! [ -e "/boot/${BOOT_IMAGE_PATH}/${BOOT_IMAGE_NAME}" ]; then
                    BOOT_IMAGE_NAME="vmlinuz-${KERNEL}"
                    BOOT_IMAGE_PATH=""
                fi
            fi

            BOOT_IMAGE_HMAC="/boot/${BOOT_IMAGE_PATH}/.${BOOT_IMAGE_NAME}.hmac"
            if ! [ -e "${BOOT_IMAGE_HMAC}" ]; then
                warn "${BOOT_IMAGE_HMAC} does not exist"
                return 1
            fi

            (cd "${BOOT_IMAGE_HMAC%/*}" && sha512hmac -c "${BOOT_IMAGE_HMAC}") || return 1
        fi
    fi

    fips_info "All initrd crypto checks done"

    : > /tmp/fipsdone

    if [ "$FIPS_MOUNTED_BOOT" = 1 ]; then
        fips_info "Unmounting /boot"
        umount /boot > /dev/null 2>&1
    else
        fips_info "Not unmounting /boot"
    fi

    return 0
}