summaryrefslogtreecommitdiffstats
path: root/modules.d/01fips/fips.sh
blob: 2c7b2e36a923d05ac1100db9cd75418fd368e1ec (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
#!/bin/sh

type getarg > /dev/null 2>&1 || . /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

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

    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
}

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
        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
}