#!/bin/sh

# Initramfs script for IMA initialzation
#
# This script is a halper used to load the external
# IMA policy and public keys used to verify the IMA
# signature.
#
# Copyright (c) 2017, Jia Zhang <lans.zhang2008@gmail.com>
# All rights reserved.
# 
# See "LICENSE" for license terms.

# Exit code:
# 0 - IMA initialiazation complete
# 1 - Kernel doesn't support securityfs
# 2 - Kernel doesn't support IMA
# 3 - There is no public key to load
# 4 - There is no IMA policy file defined
# 5 - Unable to load IMA policy file

# If root directory is not specified, the root of
# initramfs assumed.
ROOT_DIR="${1}"

SECURITYFS_DIR="${ROOT_DIR}/sys/kernel/security"

# The policy files are always placed in initramfs
IMA_POLICY=/etc/ima_policy

SECURITYFS_MOUNTED=0

print_critical() {
    printf "\033[1;35m"
    echo "$@"
    printf "\033[0m"
}

print_error() {
    printf "\033[1;31m"
    echo "$@"
    printf "\033[0m"
}

print_warning() {
    printf "\033[1;33m"
    echo "$@"
    printf "\033[0m"
}

print_info() {
    printf "\033[1;32m"
    echo "$@"
    printf "\033[0m"
}

print_verbose() {
    printf "\033[1;36m"
    echo "$@"
    printf "\033[0m"
}

trap_handler() {
    local err=$?

    print_verbose "Cleaning up with exit code $err ..."

    [ $SECURITYFS_MOUNTED -eq 1 ] &&
        umount "$SECURITYFS_DIR" 2>"${ROOT_DIR}/dev/null"
}

trap "trap_handler $?" SIGINT EXIT

if grep -q "ima_appraise=off" "${ROOT_DIR}/proc/cmdline"; then
    print_info "Skip to load the public key and IMA policy"
    exit 0
fi

if ! grep -q securityfs "${ROOT_DIR}/proc/mounts"; then
   ! mount -t securityfs none "$SECURITYFS_DIR" 2>"${ROOT_DIR}/dev/null" && {
       print_error "Unable to mount securityfs filesystem"
       exit 1
   }
   SECURITYFS_MOUNTED=1
   securityfs_dir="$SECURITYFS_DIR"
else
   securityfs_dirs="$(grep securityfs ${ROOT_DIR}/proc/mounts | awk '{print $2}')"

   # Use the first one.
   for securityfs_dir in "$securityfs_dirs"; do
       break
   done
fi

[ ! -d "$securityfs_dir/ima" ] &&
    print_info "IMA is not enabled. Exiting ..." && exit 2

keyring_id=0x`grep '\skeyring\s*\.ima: ' "${ROOT_DIR}/proc/keys" | awk '{ print $1 }'`

for key in ${ROOT_DIR}/etc/keys/x509_evm*.crt; do
    [ ! -s "$key" ] && continue

    if ! evmctl import "$key" "$keyring_id" >"${ROOT_DIR}/dev/null"; then
        print_critical "Unable to load the public key $key for IMA appraisal"
    else
        print_verbose "The external public key $key loaded for IMA appraisal"
    fi
done

# Attempt to load the default policy.
[ ! -f "${IMA_POLICY}" ] && IMA_POLICY="${IMA_POLICY}.default"

[ ! -f "${IMA_POLICY}" ] && {
    print_warning "No IMA policy file defined"
    exit 4
}

echo "${IMA_POLICY}" > "$securityfs_dir/ima/policy" && {
    exit 0
} || {
    print_critical "Unable to load the IMA policy ${IMA_POLICY}"
    exit 5
}
