#
# Revision history:
#
+# 0.5.1 -- Added --shim option
# 0.5.0 -- Added --usedefault & --drivers options & changed "esp" option to "--esp"
# 0.4.5 -- Fixed check for rEFItBlesser in OS X
# 0.4.2 -- Added notice about BIOS-based OSes & made NVRAM changes in Linux smarter
TargetDir=/EFI/refind
EtcKeysDir=/etc/refind.d/keys
-SBModeInstall=0
+LocalKeysBase="refind_local"
+ShimSource="none"
+TargetX64="refind_x64.efi"
+TargetIA32="refind_ia32.efi"
+LocalKeys=0
+DeleteRefindDir=0
#
# Functions used by both OS X and Linux....
;;
--usedefault) TargetDir=/EFI/BOOT
TargetPart=$2
+ TargetX64="bootx64.efi"
+ TargetIA32="bootia32.efi"
+ shift
+ ;;
+ --shim) ShimSource=$2
shift
;;
--drivers) InstallDrivers=1
;;
- * ) echo "Usage: $0 [--esp | --usedefault {device-file}] [--drivers]"
- echo "Aborting!"
- exit 1
+ --localkeys) LocalKeys=1
+ ;;
+ * ) echo "Usage: $0 [--esp | --usedefault {device-file}] [--drivers] "
+ echo " [--localkeys] [--shim {shim-filename}]"
+ echo "Aborting!"
+ exit 1
esac
shift
done
} # GetParams()
# Abort if the rEFInd files can't be found.
-# Also sets $ConfFile to point to the configuration file, and
-# $IconsDir to point to the icons directory
+# Also sets $ConfFile to point to the configuration file,
+# $IconsDir to point to the icons directory, and
+# $ShimSource to the source of the shim.efi file (if necessary).
CheckForFiles() {
# Note: This check is satisfied if EITHER the 32- or the 64-bit version
# is found, even on the wrong platform. This is because the platform
IconsDir=$ThisDir/icons
else
echo "The icons directory is missing! Aborting installation!"
+ exit 1
+ fi
+
+ if [[ $ShimSource != "none" ]] ; then
+ if [[ -f $ShimSource ]] ; then
+ TargetX64="grubx64.efi"
+ MokManagerSource=`dirname $ShimSource`/MokManager.efi
+ else
+ echo "The specified shim file, $ShimSource, doesn't exist!"
+ echo "Aborting installation!"
+ exit 1
+ fi
fi
} # CheckForFiles()
+# Helper for CopyRefindFiles; copies shim files (including MokManager, if it's
+# available) to target.
+CopyShimFiles() {
+ cp $ShimSource $InstallDir/$TargetDir/$TargetShim
+ if [[ $? != 0 ]] ; then
+ Problems=1
+ fi
+ if [[ -f $MokManagerSource ]] ; then
+ cp $MokManagerSource $InstallDir/$TargetDir/
+ fi
+ if [[ $? != 0 ]] ; then
+ Problems=1
+ fi
+} # CopyShimFiles()
+
+# Copy the public keys to the installation medium
+CopyKeys() {
+ if [[ $LocalKeys == 1 ]] ; then
+ cp $EtcKeysDir/$LocalKeysBase.cer $InstallDir/$TargetDir
+ cp $EtcKeysDir/$LocalKeysBase.crt $InstallDir/$TargetDir
+ else
+ cp $ThisDir/refind.cer $InstallDir/$TargetDir
+ cp $ThisDir/refind.crt $InstallDir/$TargetDir
+ fi
+} # CopyKeys()
+
# Copy the rEFInd files to the ESP or OS X root partition.
# Sets Problems=1 if any critical commands fail.
CopyRefindFiles() {
mkdir -p $InstallDir/$TargetDir &> /dev/null
if [[ $TargetDir == '/EFI/BOOT' ]] ; then
- cp $RefindDir/refind_ia32.efi $InstallDir/$TargetDir/bootia32.efi 2> /dev/null
+ cp $RefindDir/refind_ia32.efi $InstallDir/$TargetDir/$TargetIA32 2> /dev/null
if [[ $? != 0 ]] ; then
echo "Note: IA32 (x86) binary not installed!"
fi
- cp $RefindDir/refind_x64.efi $InstallDir/$TargetDir/bootx64.efi 2> /dev/null
+ cp $RefindDir/refind_x64.efi $InstallDir/$TargetDir/$TargetX64 2> /dev/null
if [[ $? != 0 ]] ; then
Problems=1
fi
+ if [[ $ShimSource != "none" ]] ; then
+ TargetShim="bootx64.efi"
+ CopyShimFiles
+ fi
if [[ $InstallDrivers == 1 ]] ; then
cp -r $RefindDir/drivers_* $InstallDir/$TargetDir/
fi
Refind=""
- cp $ThisDir/refind.cer $InstallDir/$TargetDir
- cp $ThisDir/refind.crt $InstallDir/$TargetDir
+ CopyKeys
elif [[ $Platform == 'EFI32' ]] ; then
- cp $RefindDir/refind_ia32.efi $InstallDir/$TargetDir
+ cp $RefindDir/refind_ia32.efi $InstallDir/$TargetDir/$TargetIA32
if [[ $? != 0 ]] ; then
Problems=1
fi
fi
Refind="refind_ia32.efi"
elif [[ $Platform == 'EFI64' ]] ; then
- cp $RefindDir/refind_x64.efi $InstallDir/$TargetDir
+ cp $RefindDir/refind_x64.efi $InstallDir/$TargetDir/$TargetX64
if [[ $? != 0 ]] ; then
Problems=1
fi
cp -r $RefindDir/drivers_x64/*_x64.efi $InstallDir/$TargetDir/drivers_x64/
fi
Refind="refind_x64.efi"
- cp $ThisDir/refind.cer $InstallDir/$TargetDir
- cp $ThisDir/refind.crt $InstallDir/$TargetDir
- if [[ $SBModeInstall == 1 ]] ; then
- echo "Storing copies of rEFInd Secure Boot public keys in $EtcKeysDir"
- mkdir -p $EtcKeysDir
- cp $ThisDir/refind.cer $EtcKeysDir
- cp $ThisDir/refind.crt $EtcKeysDir
+ CopyKeys
+ if [[ $ShimSource != "none" ]] ; then
+ TargetShim=`basename $ShimSource`
+ CopyShimFiles
+ Refind=$TargetShim
+ if [[ $LocalKeys == 0 ]] ; then
+ echo "Storing copies of rEFInd Secure Boot public keys in $EtcKeysDir"
+ mkdir -p $EtcKeysDir
+ cp $ThisDir/refind.cer $EtcKeysDir
+ cp $ThisDir/refind.crt $EtcKeysDir
+ fi
fi
else
echo "Unknown platform! Aborting!"
exit 1
fi
- echo "Copied rEFInd binary file $Refind"
+ echo "Copied rEFInd binary files"
echo ""
if [[ -d $InstallDir/$TargetDir/icons ]] ; then
rm -rf $InstallDir/$TargetDir/icons-backup &> /dev/null
fi
if [[ -f $InstallDir/$TargetDir/refind.conf ]] ; then
echo "Existing refind.conf file found; copying sample file as refind.conf-sample"
- echo "to avoid collision."
+ echo "to avoid overwriting your customizations."
echo ""
cp -f $ConfFile $InstallDir/$TargetDir
if [[ $? != 0 ]] ; then
Problems=1
fi
fi
+ if [[ $DeleteRefindDir == 1 ]] ; then
+ echo "Deleting the temporary directory $RefindDir"
+ rm -r $RefindDir
+ fi
} # CopyRefindFiles()
# Mount the partition the user specified with the --usedefault option
# Now a series of Linux support functions....
#
-# Check for evidence that we're running in Secure Boot mode. If so, warn the
-# user and confirm installation.
-# TODO: Perform a reasonable Secure Boot installation.
+# Check for evidence that we're running in Secure Boot mode. If so, and if
+# appropriate options haven't been set, warn the user and offer to abort.
+# If we're NOT in Secure Boot mode but the user HAS specified the --shim
+# or --localkeys option, warn the user and offer to abort.
CheckSecureBoot() {
- VarFile=`ls -ld /sys/firmware/efi/vars/SecureBoot* 2> /dev/null`
- if [[ -n $VarFile && $TargetDir != '/EFI/BOOT' ]] ; then
+ VarFile=`ls -d /sys/firmware/efi/vars/SecureBoot* 2> /dev/null`
+ if [[ -n $VarFile && $TargetDir != '/EFI/BOOT' && $ShimSource == "none" ]] ; then
echo ""
- echo "CAUTION: The computer seems to have been booted with Secure Boot active."
- echo "Although rEFInd, when installed in conjunction with the shim boot loader, can"
- echo "work on a Secure Boot computer, this installation script doesn't yet support"
- echo "direct installation in a way that will work on such a computer. You may"
- echo "proceed with installation with this script, but if you intend to boot with"
- echo "Secure Boot active, you must then reconfigure your boot programs to add shim"
- echo "to the process. Alternatively, you may terminate this script and do a manual"
- echo "installation, as described at http://www.rodsbooks.com/refind/secureboot.html."
+ echo "CAUTION: The computer seems to have been booted with Secure Boot active, but"
+ echo "you haven't specified a valid shim.efi file source. The resulting installation"
+ echo "will not boot unless you disable Secure Boot. You may continue, but you should"
+ echo "consider using --shim to specify a working shim.efi file. You can read more"
+ echo "about this topic at http://www.rodsbooks.com/refind/secureboot.html."
echo ""
echo -n "Do you want to proceed with installation (Y/N)? "
read ContYN
echo "OK; continuing with the installation..."
else
exit 0
- SBModeInstall=1
fi
fi
+
+ if [[ $ShimSource != "none" && ! -n $VarFile ]] ; then
+ echo ""
+ echo "You've specified installing using a shim.efi file, but your computer does not"
+ echo "appear to be running in Secure Boot mode. Although installing in this way"
+ echo "should work, it's unnecessarily complex. You may continue, but unless you"
+ echo "plan to enable Secure Boot, you should consider stopping and omitting the"
+ echo "--shim option. You can read more about this topic at"
+ echo "http://www.rodsbooks.com/refind/secureboot.html."
+ echo ""
+ echo -n "Do you want to proceed with installation (Y/N)? "
+ read ContYN
+ if [[ $ContYN == "Y" || $ContYN == "y" ]] ; then
+ echo "OK; continuing with the installation..."
+ else
+ exit 0
+ fi
+ fi
+
+ if [[ $LocalKeys != 0 && ! -n $VarFile ]] ; then
+ echo ""
+ echo "You've specified re-signing your rEFInd binaries with locally-generated keys,"
+ echo "but your computer does not appear to be running in Secure Boot mode. The"
+ echo "keys you generate will be useless unless you enable Secure Boot. You may"
+ echo "proceed with this installation, but before you do so, you may want to read"
+ echo "more about it at http://www.rodsbooks.com/refind/secureboot.html."
+ echo ""
+ echo -n "Do you want to proceed with installation (Y/N)? "
+ read ContYN
+ if [[ $ContYN == "Y" || $ContYN == "y" ]] ; then
+ echo "OK; continuing with the installation..."
+ else
+ exit 0
+ fi
+ fi
+
+} # CheckSecureBoot()
+
+# Check for the presence of locally-generated keys from a previous installation in
+# $EtcKeysDir (/etc/refind.d/keys). If they're not present, generate them using
+# openssl.
+GenerateKeys() {
+ PrivateKey=$EtcKeysDir/$LocalKeysBase.key
+ CertKey=$EtcKeysDir/$LocalKeysBase.crt
+ DerKey=$EtcKeysDir/$LocalKeysBase.cer
+ OpenSSL=`which openssl 2> /dev/null`
+
+ # Do the work only if one or more of the necessary keys is missing
+ # TODO: Technically, we don't need the DerKey; but if it's missing and openssl
+ # is also missing, this will fail. This could be improved.
+ if [[ ! -f $PrivateKey || ! -f $CertKey || ! -f $DerKey ]] ; then
+ echo "Generating a fresh set of local keys...."
+ mkdir -p $EtcKeysDir
+ chmod 0700 $EtcKeysDir
+ if [[ ! -x $OpenSSL ]] ; then
+ echo "Can't find openssl, which is required to create your private signing keys!"
+ echo "Aborting!"
+ exit 1
+ fi
+ if [[ -f $PrivateKey ]] ; then
+ echo "Backing up existing $PrivateKey"
+ cp -f $PrivateKey $PrivateKey.backup 2> /dev/null
+ fi
+ if [[ -f $CertKey ]] ; then
+ echo "Backing up existing $CertKey"
+ cp -f $CertKey $CertKey.backup 2> /dev/null
+ fi
+ if [[ -f $DerKey ]] ; then
+ echo "Backing up existing $DerKey"
+ cp -f $DerKey $DerKey.backup 2> /dev/null
+ fi
+ $OpenSSL req -new -x509 -newkey rsa:2048 -keyout $PrivateKey -out $CertKey \
+ -nodes -days 3650 -subj "/CN=Locally-generated rEFInd key/"
+ $OpenSSL x509 -in $CertKey -out $DerKey -outform DER
+ chmod 0600 $PrivateKey
+ else
+ echo "Using existing local keys...."
+ fi
+}
+
+# Sign a single binary. Requires parameters:
+# $1 = source file
+# $2 = destination file
+# Also assumes that the SBSign, PESign, UseSBSign, UsePESign, and various key variables are set
+# appropriately.
+# Aborts script on error
+SignOneBinary() {
+ if [[ $UseSBSign == 1 ]] ; then
+ $SBSign --key $PrivateKey --cert $CertKey --output $2 $1
+ if [[ $? != 0 ]] ; then
+ echo "Problem signing the binary $1! Aborting!"
+ exit 1
+ fi
+ else
+ echo "PESign code not yet written; aborting!"
+ exit 1
+ fi
+}
+
+# Re-sign the x86-64 binaries with a locally-generated key, First look for appropriate
+# key files in $EtcKeysDir. If they're present, use them to re-sign the binaries. If
+# not, try to generate new keys and store them in $EtcKeysDir.
+ReSignBinaries() {
+ SBSign=`which sbsign 2> /dev/null`
+ TempDir="/tmp/refind_local"
+ if [[ ! -x $SBSign ]] ; then
+ echo "Can't find either sbsign or pesign; one of these is required to sign rEFInd"
+ echo "with your own keys! Aborting!"
+ exit 1
+ fi
+ GenerateKeys
+ mkdir -p $TempDir/drivers_x64
+ cp $RefindDir/refind.conf-sample $TempDir
+ cp $RefindDir/refind_ia32.efi $TempDir
+ cp -a $RefindDir/drivers_ia32 $TempDir
+ SignOneBinary $RefindDir/refind_x64.efi $TempDir/refind_x64.efi
+ for Driver in `ls $RefindDir/drivers_x64/*.efi` ; do
+ TempName=`basename $Driver`
+ SignOneBinary $Driver $TempDir/drivers_x64/$TempName
+ done
+ RefindDir=$TempDir
+ DeleteRefindDir=1
}
# Identifies the ESP's location (/boot or /boot/efi); aborts if
EfiEntryFilename=`echo ${EntryFilename//\//\\\}`
EfiEntryFilename2=`echo ${EfiEntryFilename} | sed s/\\\\\\\\/\\\\\\\\\\\\\\\\/g`
ExistingEntry=`$Efibootmgr -v | grep $EfiEntryFilename2`
- if [[ $ExistingEntry ]] ; then
+ # NOTE: Below protects against duplicate entries, but only for non-Secure Boot
+ # installations.
+ # TODO: Improve to detect & protect against duplicating a Secure Boot entry.
+ if [[ $ExistingEntry && $ShimSource == "none" ]] ; then
ExistingEntryBootNum=`echo $ExistingEntry | cut -c 5-8`
FirstBoot=`$Efibootmgr | grep BootOrder | cut -c 12-15`
if [[ $ExistingEntryBootNum != $FirstBoot ]] ; then
echo "manager. If this is NOT what you want, you should use efibootmgr to"
echo "manually adjust your EFI's boot order."
$Efibootmgr -b $ExistingEntryBootNum -B &> /dev/null
- InstallIt="1"
+ InstallIt="1"
fi
else
InstallIt="1"
CpuType=`uname -m`
if [[ $CpuType == 'x86_64' ]] ; then
Platform="EFI64"
+ if [[ $LocalKeys == 1 ]] ; then
+ ReSignBinaries
+ fi
elif [[ $CpuType == 'i386' || $CpuType == 'i486' || $CpuType == 'i586' || $CpuType == 'i686' ]] ; then
+ if [[ $ShimSource != "none" && $TargetDir != "/BOOT/EFI" ]] ; then
+ echo ""
+ echo "CAUTION: Neither rEFInd nor shim currently supports 32-bit systems, so you"
+ echo "should not use the --shim option to install on such systems. Aborting!"
+ echo ""
+ exit 1
+ fi
Platform="EFI32"
echo
echo "CAUTION: This Linux installation uses a 32-bit kernel. 32-bit EFI-based"
ThisDir="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
RefindDir="$ThisDir/refind"
ThisScript="$ThisDir/`basename $0`"
-CheckForFiles
if [[ `whoami` != "root" ]] ; then
echo "Not running as root; attempting to elevate privileges via sudo...."
- sudo $ThisScript $1 $2 $3
+ sudo $ThisScript $1 $2 $3 $4 $5 $6
if [[ $? != 0 ]] ; then
echo "This script must be run as root (or using sudo). Exiting!"
exit 1
exit 0
fi
fi
+CheckForFiles
if [[ $OSName == 'Darwin' ]] ; then
+ if [[ $ShimDir != "none" ]] ; then
+ echo "The --shim option is not supported on OS X! Exiting!"
+ exit 1
+ fi
+ if [[ $LocalKeys != 0 ]] ; then
+ echo "The --localkeys option is not supported on OS X! Exiting!"
+ exit 1
+ fi
InstallOnOSX $1
elif [[ $OSName == 'Linux' ]] ; then
InstallOnLinux