#!/bin/bash
# Copyright (c) 2019 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

set -euo pipefail

RPM_OSTREE_BIN="$(command -v rpm-ostree)"

# check_and_enable looks to see if the kernel arg is already there. If it is not
# there, the argument will be added
function check_and_enable() {
    CHECK_FOR_ARG=$1
    # Add the kernel arg if it does not exist
    ARG_CHECK="$(${RPM_OSTREE_BIN} kargs | grep -q "${CHECK_FOR_ARG}"; echo $?)"
    if [[ ${ARG_CHECK} != 0 ]]; then
        ${RPM_OSTREE_BIN} kargs --append="${CHECK_FOR_ARG}"
    fi
}

# check_and_disable looks to see if the kernel arg is already there. If it is
# there, the argument will be delete.
function check_and_disable() {
    CHECK_FOR_ARG=$1
    # Remove the kernel arg if it exists
    ARG_CHECK="$(${RPM_OSTREE_BIN} kargs | grep -q "${CHECK_FOR_ARG}"; echo $?)"
    if [[ ${ARG_CHECK} == 0 ]]; then
        ${RPM_OSTREE_BIN} kargs --delete="${CHECK_FOR_ARG}"
    fi
}

# is_ostree_system checks if /run/ostree-booted exists
function is_ostree_system() {
    if [ ! -e /run/ostree-booted ]; then
        echo "This does not seem to be an ostree owned system."
        echo "Exiting without any changes."
        exit 1
    fi
}


function enable_fips() {
    # If FIPS is already enabled, exit out quickly
    if [ "$(cat /proc/sys/crypto/fips_enabled)" == 1 ]; then
        exit 0
    fi
    # Set the crypto policy to FIPS
    update-crypto-policies --set FIPS
    # Don't do bootconfig as the FIPS tools don't understand rpm-ostree
    fips-mode-setup --enable --no-bootcfg
    # Add the kernerl args normally added by fips-mode-setup manually
    # Make sure fips=1 is present
    check_and_enable "fips=1"
    # Make sure boot=UUID=... is present
    check_and_enable "boot=UUID=${UUID}"
    # Create hmacs for the kernel and initramfs
    fipshmac /boot/ostree/*/vmlinuz*
    fipshmac /boot/ostree/*/initramfs*
}

function disable_fips() {
    # If FIPS is already disabled, exit out quickly
    if [ "$(cat /proc/sys/crypto/fips_enabled)" == 0 ]; then
        exit 0
    fi
    # Set the crypto policy back to DEFAULT
    update-crypto-policies --set DEFAULT
    # Don't do bootconfig as the FIPS tools don't understand rpm-ostree
    fips-mode-setup --disable --no-bootcfg
    # Make sure fips=1 is no longer present
    check_and_disable "fips=1"
    # Make sure boot=UUID=... is no longer present
    check_and_disable "boot=UUID=${UUID}"
    # Remove the "fips is enabled" file
    rm -f /etc/system-fips
}

function show_help() {
    echo "You must specify one of the following: enable, disable"
    exit 1
}

# Catch no commands early
if [ $# == 0 ]; then
    show_help
fi
# Ensure we are on an ostree based system
is_ostree_system
# Get the boot disk UUID
UUID="$(blkid -l -s UUID -t LABEL=boot -o value)"

if [ "$1" == "enable" ]; then
    enable_fips
elif [ "$1" == "disable" ]; then
    disable_fips
else
    show_help
fi
