Linux, Loop-AES and Optional Smartcard Based Disk Encryption
The goal is to create an encrypted linux system using TuxOnIce or uswsusp on a loop-AES encrypted harddrive that is also encrypted when suspended, optionally storing the encryption keys on PKCS#11 cryptographic tokens (Smartcards).
We would like to thank the following people:
Nigel Cunningham who made TuxOnIce available.
- Pavel Machek and Rafael J. Wysocki who made uswsusp available.
- Jari Ruusu who made loop-aes available.
- Michal Januszewski for his work on fbsplash/gensplash.
- Will Ashford who helped me in writing this document.
The following demonstrates a working method and includes a check to see if the unencrypted /boot partition was changed (leading to a possibly compromised kernel or initrd/initramfs). And If you use frame buffer splash you also get a nice bootsplash with suspend/resume status and a set of tools to compile your kernel correctly.
We hope you will find this information useful,
Alon Bar-Lev (AlonBarLev)
Required Components
TuxOnIce from http://www.tuxonice.net needed for suspending.
uswsusp from http://suspend.sf.net needed for suspending.
suspend2-userui from http://www.tuxonice.net needed for suspend/resume status report.
loop-aes from http://sourceforge.net/projects/loop-aes/ needed to encrypt a disk on-the-fly.
aespipe from http://sourceforge.net/projects/loop-aes/ needed to encrypt/decrypt partition.
gpg from http://www.gnupg.org/ needed to handle keys.
Prerequisites
Knowledge of all of these is easy to obtain with the help of google and the documentation for TuxOnIce and Loop-AES.
- You know how to compile utilities in static mode.
- You know how to create initramfs.
- Make sure you have a working initramfs and that you control the init/linuxrc script.
Make sure you have a working TuxOnIce or uswsusp environment.
- Make sure you have a working loop-aes environment, test loop on files.
- Generate keys to be used to encrypt your disk.
- Compile gpg, losetup, aespipe as static.
You have a backup of all data on your system. If something breaks you could easily lose the entire partition.
- No really, back up your data.
Partitions
In order to have a secure encrypted and suspendable environment, you will need to have at least three partitions on your hard drive.
- boot partition
- Unencrypted /boot partition, used to hold kernel, initramfs and keyfile.
- swap partition
- Encrypted swap partition, used for suspending, resuming, and safely swapping memory to disk.
- root partition
- Encrypted root partition, your data may be stored here.
Throughout this document the following partitions are used, you should replace them with your own:
- /dev/hda1 - boot
- /dev/hda2 - swap
- /dev/hda3 - root
Important Notes
We recommend that you use at least two version of your kernel: a testing one for trying out new kernels and a stable one that is known to work. An encrypted root partition makes this very important as an unbootable kernel unbootable or missing kernel modules can make it very difficult to recover your system and data. You can use the kernel option CONFIG_LOCALVERSION to give each kernel a unique name to avoid confusion. ALWAYS have a WORKING backup kernel once you have a working environment.
In this guide Loop-AES is used over cryptoloop and dm-crypt due to serious flaws in the latter two methods. Cryptoloop, dm-crypt, and Loop-AES in single-key mode are all vulnerable to a "watermarking" attack. We recommend using Loop-AES in Multi-key-v3 mode.
There are a number of ways to store the decryption keys and we try to accommodate them all. A key may be kept on a PKCS#11 cryptographic token, in a keyfile encrypted to your gpg public key, or in a keyfile encrypted with symmetric encryption. If you do not have a smartcard, you can store a keyfile on external media such as a cd-rom or usb thumb drive. Please refer to the Loop-AES documentation for instructions on generating keys and a security analysis. Because symmetric encryption is heavily dependent on the strength of the passphrase used, http://www.diceware.com is suggested for generating strong passphrases.
Beginning the Process
Outline
- Compile a new kernel with necessary patches and additions
- Test new kernel
- Create an initramfs
- Create encryption keys
- Set up the framework for Loop-AES encrypted root and swap
- Actually encrypt root and swap
- Prepare suspen framework
- Test the final product
Compiling a New Kernel
Compile your kernel with TuxOnIce or uswsusp support, initramfs support and NO loop device support.
- Compile loop-aes module.
Testing additions
Having done one of the two options above, you should now reboot into your new kernel to test the new features. Use the Loop-AES documentation to ensure that you can create a file-backed encrypted loop and that everything works properly. Don't forget to modprobe loop if you built Loop-AES as a module.
Create Initramfs
An initramfs is a lot like the familiar initrd only it uses a different archiving format (cpio instead of tar) and is accessed by the kernel slightly differently. Initramfs is capable of some things initrd just can't do and is the wave of the future.
Look at initramfs section in order to understand how to create the initramfs.
Initramfs Kernel Parameters
The linuxrc script accepts the following additional kernel parameters:
- initrd_util
- pkcs11:application:label,label,label
- use PKCS#11 smartcards.
- use gnupg files located at device. wait for device to be available.
- pkcs11:application:label,label,label
- initrd_devices=/dev/hdXN,/dev/hdYM,...
- devices to loop over.
- initrd_loopstart=N
- The first loop index (/dev/loopN)
- initrd_shell=N
- Interrupts the initramfs and drops to a shell at a specified location (rescue, install, repare), see the linuxrc for more locations.
If no initrd_* parameter is specify, the initramfs will perform regular boot with no encryption support.
Example:
initrd_util=gpgfile:/dev/hda1:/swap.gpg,/root.gpg initrd_devices=/dev/hda2,/dev/hda3 initrd_loopstart=4
This will load utilities from /dev/hda1 and map:
/dev/loop4->/dev/hda2 with key from /swap.gpg
/dev/loop5->/dev/hda3 with key from /root.gpg
Create Encryption Keys
Using a file
Run the following command:
head -c 2925 /dev/random | uuencode -m - | head -n 66 | tail -n 65 |
gpg --symmetric -a >/boot/root.gpg
Using PKCS#11 cryptographic token
Run the following command, replacing pkcs11_provider_library with your PKCS#11 provider library:
pkcs11-data --add-provider=pkcs11_provider_library --cmd=tokens
The output will include all available tokens, select the correct token, the id='...' field is the token id to be used in next command.
Run the following command, replacing pkcs11_provider_library with your PKCS#11 provider library, and token_id with the output taken from previous command, please remember to add single quote for token_id.
head -c 2925 /dev/random | uuencode -m - | head -n 66 | tail -n 65 |
gzip | pkcs11-data --add-provider=pkcs11_provider_library --cmd=import \
--token='token_id' --application=DISK --label=MY
Loop-AES Framework
At least the following need to be in your /etc/fstab for your system to boot properly. The suspended image will be written to /dev/loop4. Remember that loop4 maps to hda2 and loop5 maps to hda3. At this time be sure that your system is capable of mounting and unmounting Loop-AES volumes.
/dev/hda1 /boot ext2 defaults,noatime,ro 1 2 /dev/loop5 / ext3 noatime 0 1 /dev/loop4 none swap sw 0 0
Actual Encrypting Step
Boot your new kernel with your new initramfs and the following arguments:
root=/dev/hda3 initrd_shell=install
You should get a shell after the initramfs has setup itself correctly but before it has mounted any partitions.
This next step goes over all of /dev/hda3 (root partition) and encrypts it. Be sure that Loop-AES is functioning in your kernel, you have created the encryption keys and the encryption keys are available on the partition passed as initrd_util. If any of the above are not true it either will not work or you will lose all your data.
Encryption using gpg key
Run the following command:
mkdir /mnt
mount -o ro /dev/hda1 /mnt
dd if=/dev/hda3 bs=64k | \
aespipe -G / -K /mnt/root.gpg -e AES256 | \
dd of=/dev/hda3 bs=64k conv=notrunc
Encryption using PKCS#11 cryptographic token
First start PKCS#11 reader support, usually the following commands will do:
for mod in `cat /etc/modules/pkcs11`; do modprobe ${mod}; done
mount -t usbfs usbfs /proc/bus/usb
pcscd --force-reader-polling [--force-reader-polling requred for >=pcsc-lite-1.4.0]
Now perform the actual encryption, don't be alarmed that the key is exported to a file, since this file resides in memory, and there is no swap.
pkcs11-data --add-provider=pkcs11_provider_library --cmd=export --application=DISK --label=MY | \
gunzip > /tmp/key1
dd if=/dev/hda3 bs=64k | \
aespipe -e AES256 -p 8 8< /tmp/key1 | \
dd of=/dev/hda3 bs=64k conv=notrunc
dd if=/dev/zero of=/tmp/key1 count=10000 conv=notrunc
rm /tmp/key1
First success boot
If you use gpg key, reboot using the following arguments:
root=/dev/loop5 initrd_util=gpgfile:/dev/hda1:/root.gpg initrd_devices=/dev/hda3
initrd_loopstart=5 initrd_encmode=loop-aes
If you use PKCS#11 cryptographic token, reboot using the following arguments:
root=/dev/loop5 initrd_util=pkcs11:DISK:MY initrd_readers=openct initrd_devices=/dev/hda3 initrd_loopstart=5 initrd_encmode=loop-aes
You should be prompted for the key password and boot should succeed.
Handle swap
Now we need to encrypt your swap partition.
Create a new key for the swap partition as you have done for your root.
Reboot with the following arguments:
root=/dev/loop5 initrd_util=gpgfile:/dev/hda1:/swap.gpg,/root.gpg initrd_devices=/dev/hda2,/dev/hda3
initrd_loopstart=4 resume=/dev/loop4
You should be prompted for the password to the gpg key and boot should succeed.
After boot create swap file:
mkswap /dev/loop4 swapon -a
Boot partition integrity
Use boot-digest-mark utility in order to store the digest of the boot partition, so that you will be notified if it is changed. Configure your system to run boot-digest-check during your boot process.
These scripts are attached to this page.
Congratulations
You have a working Loop-AES environment.
Software Suspend Framework
Now to get software TuxOnIce or uswsusp to work. For TuxOnIce specify a resume= parameter at kernel command-line, for uswsusp update /etc/suspend.conf of initramfs and on your root. Both should point to your swap partition (/dev/loop4).
Specify the suspend mode at kernel command-line initrd_suspend_mode=suspend2 or initrd_suspend_mode=uswsusp.
hibernate.conf
Add the following lines to hibernate.conf.
This will check the boot partition on every resume:
OnResume 00 /usr/bin/boot-digest-check
This will unmount boot partition so it can be mounted during boot by initramfs with no data loss:
Unmount /boot Mount /boot
If you are using fbsplsah, this will activate splash during suspend/resume:
SwitchToTextMode yes FBSplash on FBSplashTheme %%THEME%%
Grub
Here is an example grub menu.lst:
default 0
timeout 30
splashimage=(hd0,0)/grub/splash.xpm.gz
title=2.6.15-suspend2-r1-debug
kernel (hd0,0)/kernel-x86-2.6.15-suspend2-r1-debug root=/dev/loop5 rootfstype=ext3 ro
video=radeonfb:1400x1050-32@60 acpi_sleep=s3_bios
splash=silent,fadein,theme:emergence quiet console=tty1
initrd_util=gpgfile:/dev/hda1:/swap.gpg,/root.gpg initrd_devices=/dev/hda2,/dev/hda3 initrd_loopstart=4
initrd_suspend_mode=suspend2 resume=swap:/dev/loop4
initrd (hd0,0)/initramfs-x86-2.6.15-suspend2-r1-debug
boot
Note: This example is using the frame buffer driver rather than vanilla vesafb. Plain vesafb would require an additional argument similar to: vga=791
Digesting boot
After you finish all your modifications, you should mark the current boot partition's digest:
boot-digest-mark
During boot or resume, you will receive a warning message if someone has tampered with your unencrypted boot partition.
Trying It All Out
Now try to hibernate... Good luck!
Extras
Using PKCS#11 cryptographic tokens
You can use PKCS#11 cryptographic tokens to hold secret key. The symmetric keys are stored on the card as private data object. The advantage to this approach is that it stronger that using RSA encryption on the symmetric keys.
Create a file /etc/pkcs11.conf with the following format, replace pkcs11_provider_library with the actual name of the PKCS#11 provider library.
--token-wait --add-provider=pkcs11_provider_library
You should have udev/mdev support and pcscd in the initramfs and specify initrd_util=pkcs11 as a kernel parameter.
A utility pkcs11-data for import/export of data objects is also required.
Since PKCS#11 provider is a dynamic library, some components of initramfs cannot be compiled statically.
You can import existing gpg keys to smartcard by using the following sequence.
Run the following command, replacing pkcs11_provider_library with your PKCS#11 provider library:
pkcs11-data --add-provider=pkcs11_provider_library --cmd=tokens
The output will include all available tokens, select the correct token, the id='...' field is the token id to be used in next command.
Run the following command, replacing pkcs11_provider_library with your PKCS#11 provider library, and token_id with the output taken from previous command, please remember to add single quote for token_id.
gpg < keyfile.gpg | gzip | pkcs11-data --add-provider=pkcs11_provider_library --cmd=import \
--token='token_id' --application=DISK --label=MY
Reverting
You can revert and decrypt your filesystems. Boot your new kernel with your new initramfs and the following arguments:
root=/dev/hda3 initrd_shell=install
You should get a shell after the initramfs has setup itself correctly but before it has mounted any partitions.
Decryption using gpg key
Run the following command:
mkdir /mnt
mount -o ro /dev/hda1 /mnt
dd if=/dev/hda3 bs=64k | \
aespipe -G / -K /mnt/keyfile.gpg -e AES256 -d | \
dd of=/dev/hda3 bs=64k conv=notrunc
Decryption using PKCS#11 cryptographic token
First start PKCS#11 reader support, usually the following commands will do:
for mod in `cat /etc/modules/pkcs11`; do modprobe ${mod}; done
mount -t usbfs usbfs /proc/bus/usb
pcscd
or
mkdir /var/run/openct
openct-control init
Now perform the actual decryption, don't be alarmed that the key is exported to a file, since this file resides in memory, and there is no swap.
pkcs11-data --add-provider=pkcs11_provider_library --cmd=export --application=DISK --label=MY | \
gunzip > /tmp/key1
dd if=/dev/hda3 bs=64k | \
aespipe -e AES256 -d -p 8 8< /tmp/key1 | \
dd of=/dev/hda3 bs=64k conv=notrunc
dd if=/dev/zero of=/tmp/key1 count=10000 conv=notrunc
rm /tmp/key1
Open issues
There is no known way to protect against sophisticated kernel attach on the unencrypted boot partition that will cause the hash check to succeed but will write your password on a location of the disk.
For now the only way to detect this is to digest the boot partition and compare it to hashes stored on the encrypted file system. This comparison is run during every boot and every resume from suspend.
In order to minimize the access of external people to this partition, consider putting the kernel and initramfs on a USB mass storage device and boot from this device. Combining with PKCS#11 cryptographic token it is the best solution.
initramfs
Busybox
The following applets should be available:
[ cat chroot chvt (Optional, splash) cp dd echo grep killall ln mdev (Optional, mdev) mkdir mknod modprobe mount sh stty (Optional, gpg) test sed switch_root tty rm rmmod umount
Structure
+-+-/
+-+- dev
| +--- fb0 (Optional, fbsplash && !mdev)
| +--- fbsplash (Optional, fbsplash && !mdev)
| +--- snapshot (Optional, uswsusp && !mdev)
| +--- hd[a-d][0-4] (Optional, !mdev)
| +--- loop? (Optional, !mdev)
+-+- etc
| +--- suspend.conf (Optional, uswsusp)
| +--- pkcs11.conf (Optional, PKCS#11)
| +--- splash (Optional, fbsplash)
| \-+- modules
| +--- boot (a list of modules required for boot)
| +--- pkcs11 (Optional, a list of modules required for PKCS#11)
| \--- suspend2 (a list of modules required for TuxOnIce resume)
+-+- lib
| \-+- modules
| \-+- `uname -r`
| +-+- block
| | \--- loop.ko
| \-+- kernel
| \-+- crypto
| \--- lzf.ko (If you want to compress your TuxOnIce image)
+-+- sbin
| +--- losetup.crypt (Must be different name than busybox losetup)
| +--- fbcondecor_helper (Optional, fbsplash)
| +--- tuxoniceui_fbsplash (Optional, TuxOnIce && fbsplash)
| +--- tuxoniceui_text (Optional, TuxOnIce && text)
| \--- resume (Optional, uswsusp)
\-+- usr
+-+- bin
| +--- aespipe
| +--- gpg (Optional, no PKCS#11)
| +--- gpg-agent (Optional, no PKCS#11, gnupg>=2.0)
| \--- pkcs11-data (Optional, PKCS#11)
+-+- lib
| \--- readers (Optional, pcsc)
\-+- sbin
+--- pcscd (Optional, pcsc)
+--- openct-control (Optional, openct)
\--- ifdhandler (Optional, openct)
linuxrc
#!/bin/sh
#
# The following standard kernel parameters are supported:
# root root device
# rootfstype root filesystem type (optional, default is auto detection)
# ro mount root as read only
# [0-9]|S runlevel
#
# The following custom kernel parameters are supported:
# initrd_kmap=kmap[:font]
# Fullpath(inside initramfs /) to kmap and font(optional).
# initrd_shell=N
# Interrupts the initramfs and drops to a shell at a specified location
# (0-none,1-on start,2-after setup,etc..), see the linuxrc for more locations.
# Special N=resuce, N=install, N=repair
# initrd_encmode=<mode>
# <empty>
# No encryption.
# loop-aes
# Enables loop-AES support.
# dm-crypt
# Enables DM-Crypt support.
# initrd_util
# pkcs11:application:label,label,label
# use PKCS#11 smartcards.
# gpgfile:device:fstype:file,file,file[:passphrase]
# use gnupg file located at device.
# wait for device to be available.
# passphrase:id,id,id (dm-crypt)
# use passphrases read from the user, each
# id signify passphrase for a device, id
# may be the same.
# keyfile:device:fstype:file,file,file (dm-crypt)
# use keyfile located at device.
# initrd_devices
# Devices for encryption setup.
# initrd_suspend_mode
# tuxonice
# uswsusp
# initrd_resume_ui
# auto (default)
# none
# fbsplash
# text
#
# initrd_readers
# pcscd
# Run pcscd.
# openct
# Run openct.
#
# loop-aes specific
# -----------------
# initrd_loopstart=N
# The first loop index (/dev/loopN)
#
# dm-crypt specific
# -----------------
# initrd_dmnames=name,name
# Logical names for dmsetup
#
# The following module lists are used files at /dev/modules:
# boot boot time modules loaded but not removed.
# tuxonice TuxOnIce modules.
# remdev modules required to access removable media.
# pkcs11 modules required to access smartcard.
#
# Examples:
# loop-AES
# root=/dev/loop5 rootfstype=ext3 ro \
# video=radeonfb:1400x1050-32@60 acpi_sleep=s3_bios \
# splash=silent,fadein,theme:livecd-2006.0 quiet console=tty1 \
# initrd_encmode=loop-aes \
# initrd_util=gpgfile:/dev/sda1:ext2:/keys/swap.gpg,/keys/root.gpg \
# initrd_devices=/dev/hda2,/dev/hda3 initrd_loopstart=4 \
# initrd_suspend_mode=tuxonice resume=swap:/dev/loop4
#
# DM-Crypt
# root=/dev/sda3 \
# video=radeonfb:1400x1050-32@60 acpi_sleep=s3_bios \
# splash=verbose,theme:livecd-2006.0 quiet console=tty1 \
# initrd_encmode=dm-crypt \
# initrd_util=gpgfile:/dev/sdb1:ext2:/keys/swap.gpg,/keys/root.gpg \
# initrd_devices=/dev/sda2,/dev/sda3 initrd_dmnames=swap,root
# initrd_suspend_mode=tuxonice resume=swap:/dev/mapper/swap
#
# History
# -----------------
# 2008.05.06 - Alon Bar-Lev
# Added initrd_readers= parameters.
# People who used smartcards need to add initrd_readers=pcscd
# to keep their configuration working.
# 2008.05.03 - Casper Biering
# Fix typo in keymap.
# 2008.03.23 - Alon Bar-Lev
# Update to newer busybox that genkernel supports.
# 2007.11.04 - Matyas Tibor
# gpg --log-file -> gpg --logger-file
# 2007.11.03 - Alon Bar-Lev
# TuxOnIce changes.
# initrd_suspend_mode must be specified on command-line.
# 2007.09.07 - Alon Bar-Lev
# Added resume= parameter support.
# Make resume device and splash aware.
# 2007.08.17 - Alon Bar-Lev
# splashutils-0.5 required.
# 2007.07.14 - Alon Bar-Lev
# Add uswsusp support.
# 2007.06.04 - Alon Bar-Lev
# Fixup newroot issue.
# Fixup initrd_shell to have rescue, install, repair.
# 2007.05.06 - Alon Bar-Lev
# Support a configuration without legacy sysfs
# 2006.10.19 - Federico ZagarzazĂș, Alon Bar-Lev
# Added support for dm-crupt
# Added support for simple files for storing key
# Switched to busybox's switch_root
# 2006.10.10 - Alon Bar-Lev
# Fixed gpg handling, many thanks to Pat Double for doing the tests.
# 2006.09.29 - Alon Bar-Lev
# Merged some of Federico work.
# Modified format for initrd_util gpgfile, added fstype.
# 2006.09.07 - Federico ZagarzazĂș
# Initial support for dm-crypt (via cryptsetup(luks))
# Some style changes.
# Added support to load kmap and font.
# Added f_echo for fancy init messages.
# Support verbose splash at bootup.
# 2006.08.25 - Alon Bar-Lev
# Switched udev into busybox mdev.
# Moved PKCS#11 application and label into command line.
# As recommended from Jari Ruusu a seperate key is required for each loop.
# 2006.05.07 - Alon Bar-Lev
# Added cipher local option.
# Removed writing the key into memory file.
# 2006.05.01 - Alon Bar-Lev
# Added gpg static passphrase option.
# 2006.04.30 - Alon Bar-Lev
# Allow linuxrc.local to supply defaults.
# 2006.04.27 - Alon Bar-Lev
# Change initrd_util parameter:
# pkcs11
# gpgfile:device:file
# Support for wait for util device if gpgfile.
# No need to have stty anymore since gpg does it for us.
# 2006.02.01 - Alon Bar-Lev
# Support of disabling selected stages.
# 2006.01.24 - Alon Bar-Lev
# Move to fbsplashds in daemon mode,
# since none-daemon mode does not paint splash currectly.
# 2005.11.24 - Alon Bar-Lev
# Added udev support.
# Added PKCS#11 support.
# 2005.06.07 - Alon Bar-Lev
# Written template from gentoo.org.
#
# don't edit
global_splash_verbose=0
global_mdev_active=0
# colors
color_black='\x1b[30;01m'
color_red='\x1b[31;01m'
color_green='\x1b[32;01m'
color_yellow='\x1b[33;01m'
color_blue='\x1b[34;01m'
color_off='\x1b[0;0m'
#
# The following variables can(and should) be
# overidden by cryptfs
#
# General
cfg_setup_initramfs=1 # Setup initramfs enviroment.
cfg_install_applets=1 # Install busybox applets at runtime(creates symlinks for all applets
# that are compiled into busybox).
cfg_start_mdev=1 # Start mdev(Busybox's mini-udev implementation)
cfg_shell_checkpoint=0 # Shell checkpoint (initrd_shell)
cfg_tmp_keys_rep=/tmp/keys # Where keys are temporary stored
cfg_tmp_keys_stat=/tmp/keys # Where keys status stored
cfg_kmap= # Load kmap (initrd_kmap)
cfg_font= # Load font (initrd_kmap)
cfg_init=/sbin/init # Init to execute from root device
cfg_newroot=/newroot # Mountpoint for root
cfg_remmnt=/mnt # Mountpoint for removable device
cfg_root_device= # Root device (root)
cfg_root_mode=ro # Root mount mode (ro)
cfg_root_type= # Root filesystem type (rootfstype)
cfg_resume_device= # Resume device.
cfg_hand_root_control=1 # Execute init from root device
cfg_util_type= # Type of utility (gpgfile,pkcs11,passphrase,keyfile) (initrd_util)
cfg_remdev= # Device assigned to removable media for gpgfile utility. (initrd_util=gpgfile)
cfg_remdev_fs= # Device assigned to removable media for gpgfile utility. (initrd_util=gpgfile)
cfg_gpg_files= # Files, one for each loop/device, comma separated (initrd_util=gpgfile)
cfg_gpg_pass= # Static passphrase (initrd_util=gpgfile)
cfg_pkcs11_application= # PKCS#11 data object application name (initrd_util=pkcs11)
cfg_pkcs11_labels= # PKCS#11 data object labels, one for each loop, (initrd_util=pkcs11)
cfg_pkcs11_pass= # PKCS#11 PIN (initrd_util=pkcs11)
cfg_passphrase_id= # Passphrase comma separated ides (initrd_util=passphrase)
cfg_devices= # Devices to decrypt (initrd_devices)
cfg_suspend_mode= # Type of suspend/resume to use
cfg_resume_ui=auto # Type of ui for resume
# loop-AES
cfg_loopaes=0 # 1 == loopAES support enabled (initrd_encmode=loop-aes)
cfg_loopstart=0 # Loop start index (initrd_loopstart)
cfg_cipher="AES256" # Encryption cipher
# dm-crypt
cfg_dmcrypt=0 # 1 == dm-crypt support enabled (initrd_encmode=dm-crypt)
cfg_dmnames= # Logical names of DM crypt
cfg_keyfile_files= # Files(keyfiles), one for each device, comma separated (initrd_util=keyfile)
# readers
cfg_openct=0 # Enable openct
cfg_pcscd=0 # Enable pcscd
if [ -e /linuxrc.local ]; then
. /linuxrc.local
fi
f_parse_head() {
local s="${1}"
local d="${2}"
echo "${s%%${d}*}"
}
f_parse_remove_head() {
local s="${1}"
local d="${2}"
local r="${s#*${d}}"
if [ "${r}" = "${s}" ]; then
echo ""
else
echo "${r}"
fi
}
f_echo() {
local type="${1}"
local msg="${2}"
local opts="${3}"
local eopts
local colors_on
local colors_off
if [ "${cfg_color_msg}" != 0 ]; then
colors_off="${color_off}"
case "${type}" in
err)
colors_on="${color_red}"
;;
inf)
colors_on="${color_yellow}"
;;
msg|*)
colors_on="${color_green}"
;;
esac
local o
for o in $(echo "${opts}" | sed 's/,/ /'); do
case "${o}" in
nonl)
eopts="$eopts -n"
;;
esac
done
fi
echo ${eopts} -e " ${colors_on}*${color_off} ${msg}"
}
f_modprobe_group() {
local group="${1}"
# Add -k on >=busybox-1.8
[ -f "/etc/modules/${group}" ] && cat "/etc/modules/${group}" | xargs -n1 modprobe > /dev/null 2>&1
}
f_rmmod_group() {
local group="${1}"
# use modprobe -r on >=busybox-1.8
[ -f "/etc/modules/${group}" ] && cat "/etc/modules/${group}" | xargs -n1 rmmod > /dev/null 2>&1
rmmod -a
}
f_killallwait() {
local p="${1}"
while killall -q -3 "${p}"; do
sleep 1
done
}
f_die() {
local message="${1}"
f_splash_verbose
f_echo err "${message}"
exec /bin/sh
}
f_shell_checkpoint() {
local level="${1}"
if [ "${cfg_shell_checkpoint}" = "${level}" ]; then
f_splash_verbose
exec /bin/sh
fi
}
f_splash_verbose() {
[ "${global_splash_verbose}" = 0 ] && chvt 1
}
f_splash_silent() {
[ "${global_splash_verbose}" = 0 ] && chvt 2
}
f_splash_message() {
local msg="${1}"
[ "${global_splash_verbose}" = 0 ] && BOOT_MSG="${msg}" fbcondecor_helper 2 'repaint'
}
f_loadkmap() {
if [ -n "${cfg_kmap}" ]; then
if [ -e "${cfg_kmap}" ]; then
loadkmap < "${cfg_kmap}"
else
f_die "Error: keymap \"${cfg_kmap}\" does not exist."
fi
fi
if [ -n "${cfg_font}" ]; then
if [ -e "${cfg_font}" ]; then
loadfont < "${cfg_font}"
else
f_die "Error: font \"${cfg_font}\" does not exist."
fi
fi
}
f_hand_root_control() {
[ -d "${cfg_newroot}" ] || mkdir "${cfg_newroot}"
mount -o "${cfg_root_mode}" ${cfg_root_type:+-t "${cfg_root_type}"} \
"${cfg_root_device}" "${cfg_newroot}" || f_die "Cannot mount root filesystem"
f_stop_mdev
umount /proc
umount /sys
exec switch_root -c "/dev/console" "${cfg_newroot}" "${cfg_init}" ${init_arg}
}
f_setup_mdev() {
if [ -x /sbin/mdev ]; then
/sbin/mdev -s
echo /sbin/mdev > /proc/sys/kernel/hotplug
global_mdev_active=1
fi
}
f_stop_mdev() {
if [ ${global_mdev_active} != 0 ]; then
echo > /proc/sys/kernel/hotplug
fi
}
f_reset_environment() {
export PATH="${OLDPATH}"
}
f_mount_remdev() {
f_modprobe_group "remdev"
f_shell_checkpoint 302
local first_time=1
while ! mount -n -o ro ${cfg_remdev_fs:+-t "${cfg_remdev_fs}"} "${cfg_remdev}" "${cfg_remmnt}" > /dev/null 2>&1; do
if [ ${first_time} != 0 ]; then
f_echo inf "Please insert removable device ${cfg_remdev}..."
first_time=0
fi
sleep 2
done
f_shell_checkpoint 303
}
f_umount_remdev() {
umount -n "${cfg_remmnt}"
f_rmmod_group "remdev"
}
f_resume_uswsusp() {
local args
local features="$(resume --version | grep FEATURES)"
[ -n "${cfg_resume_device}" ] && args="${args} --resume_device=${cfg_resume_device}"
echo "${features}" | grep fbsplash > /dev/null && [ -n "${splash_theme}" -a "${global_splash_verbose}" = 0 ] && [ "${cfg_resume_ui}" = "auto" -o "${cfg_resume_ui}" = "fbsplash" ] && args="${args} --parameter=\"splash=y\""
eval resume ${args}
}
f_resume_tuxonice() {
local tuxonice_userui_program="/sys/power/tuxonice/user_interface/program"
local tuxonice_do_resume="/sys/power/tuxonice/do_resume"
local tuxonice_resume="/sys/power/tuxonice/resume"
#
# Backward compatibility
#
if [ -e /proc/suspend2 ]; then
tuxonice_userui_program="/sys/power/suspend2/user_interface/program"
tuxonice_do_resume="/sys/power/suspend2/do_resume"
tuxonice_resume="/sys/power/suspend2/resume2"
elif [ -e /proc/suspend2 ]; then
tuxonice_userui_program="/proc/suspend2/userui_program"
tuxonice_do_resume="/proc/suspend2/do_resume"
tuxonice_resume="/proc/suspend2/resume2"
fi
f_modprobe_group "tuxonice"
[ -n "${splash_theme}" ] && ln -s "/etc/splash/${splash_theme}" /etc/splash/suspend2
if [ "${cfg_resume_ui}" != "none" -a "${global_splash_verbose}" = 0 ]; then
if [ "${cfg_resume_ui}" = "auto" ]; then
if [ -z "${splash_theme}" ]; then
if which "suspend2ui_text" > /dev/null 2>&1; then
which "suspend2ui_text" > "${tuxonice_userui_program}"
fi
if which "tuxonice_text" > /dev/null 2>&1; then
which "tuxonice_text" > "${tuxonice_userui_program}"
fi
else
if which "suspend2ui_fbsplash" > /dev/null 2>&1; then
which "suspend2ui_fbsplash" > "${tuxonice_userui_program}"
fi
if which "tuxonice_fbsplash" > /dev/null 2>&1; then
which "tuxonice_fbsplash" > "${tuxonice_userui_program}"
fi
fi
else
if which "suspend2ui_${cfg_resume_ui}"; > /dev/null 2>&1; then
which "suspend2ui_${cfg_resume_ui}" > "${tuxonice_userui_program}"
fi
if which "tuxonice_${cfg_resume_ui}"; > /dev/null 2>&1; then
which "tuxonice_${cfg_resume_ui}" > "${tuxonice_userui_program}"
fi
fi
fi
[ -n "${cfg_resume_device}" ] && echo "${cfg_resume_device}" > "${tuxonice_resume}"
echo > "${tuxonice_do_resume}"
f_rmmod_group tuxonice
}
f_resume() {
case "${cfg_suspend_mode}" in
tuxonice) f_resume_tuxonice;;
uswsusp) f_resume_uswsusp;;
esac
}
f_keys_read_pkcs11() {
f_shell_checkpoint 101
f_modprobe_group "pkcs11"
if [ "${cfg_pcscd}" != "0" ]; then
mount -n -t usbfs usbfs /proc/bus/usb
sleep 3 # usbfs takes time
pcscd --force-reader-polling
fi
if [ "${cfg_openct}" != "0" ]; then
mkdir -p /var/run/openct
openct-control init
fi
f_shell_checkpoint 102
local labels="${cfg_pkcs11_labels}"
local index=0
local pkcs11_objects
while [ -n "${labels}" ]; do
local label
label=$(f_parse_head "${labels}" ",")
labels=$(f_parse_remove_head "${labels}" ",")
pkcs11_objects="${pkcs11_objects} \"--application=${cfg_pkcs11_application}\" \"--label=${label}\" \"--file=${cfg_tmp_keys_rep}/${index}.raw\""
index=$((${index}+1))
done
while ! eval pkcs11-data $(cat /etc/pkcs11.conf) --cmd=export ${pkcs11_objects}; do
f_echo msg "Please try again."
done
local key
for key in "${cfg_tmp_keys_rep}"/*; do
gunzip -c "${key}" > $(echo ${key} | sed 's/.raw//').key
done
f_shell_checkpoint 103
if [ "${cfg_pcscd}" != "0" ]; then
f_killallwait pcscd
while ! umount -n /proc/bus/usb > /dev/null 2>&1; do
sleep 1
done
fi
if [ "${cfg_openct}" != "0" ]; then
openct-control shutdown > /dev/null
fi
f_shell_checkpoint 104
f_rmmod_group "pkcs11"
f_shell_checkpoint 105
}
f_keys_read_gpg() {
local index=0
local bad_pass=1
while [ ${bad_pass} != 0 ]; do
if [ -z "${cfg_gpg_pass}" ]; then
stty -echo
echo -n "Passphrase: "
read cfg_gpg_pass
echo
stty echo
fi
bad_pass=0
local file
for file in $(echo "${cfg_gpg_files}" | sed 's/,/ /g'); do
if ! [ -f "${cfg_remmnt}/${file}" ]; then
f_die "Cannot access ${cfg_remdev}:${file}"
fi
if ! echo "${cfg_gpg_pass}" | \
gpg --homedir / \
--batch \
--logger-file /dev/null \
--passphrase-fd 0 \
--decrypt "${cfg_remmnt}/${file}" > \
"${cfg_tmp_keys_rep}/${index}.key"; then
bad_pass=1
else
index=$((${index}+1))
fi
done
unset cfg_gpg_pass
if [ ${bad_pass} != 0 ]; then
echo "Please try again." >&2
fi
done
}
f_keys_read_keyfile() {
local index=0 file
for file in $(echo "${cfg_keyfile_files}" | sed 's/,/ /g'); do
if ! [ -f "${cfg_remmnt}/${file}" ]; then
f_die "Cannot access ${cfg_remdev}:${file}"
else
cp "${cfg_remmnt}/${file}" "${cfg_tmp_keys_rep}/${index}.key"
fi
index=$((${index}+1))
done
}
f_keys_read_passphrase() {
local first_time=0
local index=0
local id
#
# If no rejects assume first time
#
[ "$(find "${cfg_tmp_keys_stat}")" = "${cfg_tmp_keys_stat}" ] && first_time=1
for id in $(echo "${cfg_passphrase_id}" | sed 's/,/ /g'); do
#
# If first time or rejected
#
if [ "${first_time}" != 0 -o -f "${cfg_tmp_keys_stat}/${index}.rejected" ]; then
rm -f "${cfg_tmp_keys_stat}/${index}.rejected" > /dev/null 2>&1
if [ -f "${cfg_tmp_keys_rep}/passphrase_${id}" ]; then
cp "${cfg_tmp_keys_rep}/passphrase_${id}" "${cfg_tmp_keys_rep}/${index}.key"
else
local password
stty -echo
echo -n "Passphrase (${id}): "
read password
echo
stty echo
echo "${password}" > "${cfg_tmp_keys_rep}/${index}.key"
echo "${password}" > "${cfg_tmp_keys_rep}/passphrase_${id}"
fi
fi
index=$((${index}+1))
done
}
f_keys_clean() {
if [ -d "${cfg_tmp_keys_rep}" ]; then
local i
for i in "${cfg_tmp_keys_rep}"/*; do
dd if=/dev/zero "of=${i}" conv=notrunc bs=1024 count=10 > /dev/null 2>&1
done
rm -fr "${cfg_tmp_keys_rep}"
fi
}
f_keys_read() {
[ -d "${cfg_tmp_keys_rep}" ] || mkdir -p "${cfg_tmp_keys_rep}"
[ -d "${cfg_tmp_keys_stat}" ] || mkdir -p "${cfg_tmp_keys_stat}"
f_shell_checkpoint 201
case "${cfg_util_type}" in
pkcs11)
f_keys_read_pkcs11
;;
gpgfile)
f_mount_remdev
f_keys_read_gpg
f_umount_remdev
;;
passphrase)
f_keys_read_passphrase
;;
keyfile)
f_mount_remdev
f_keys_read_keyfile
f_umount_remdev
;;
esac
f_shell_checkpoint 202
}
f_opendevices_loopaes() {
local error=0
#
# map loop devices
#
local index=0
local device
for device in $(echo "${cfg_devices}" | sed 's/,/ /g'); do
local keyfile="${cfg_tmp_keys_rep}/${index}.key"
if [ -f "${keyfile}" ]; then
if ! losetup.crypt -p 0 -e "${cfg_cipher}" \
"/dev/loop$((${cfg_loopstart}+${index}))" \
"${device}" < "${keyfile}"; then
touch "${cfg_tmp_keys_stat}/${index}.rejected"
f_echo err "Cannot loop ${device}"
error=1
fi
fi
index="$((${index}+1))"
done
return "${error}"
}
f_opendevices_dmcrypt() {
local error=0
#
# map loop devices
#
local index=0
local names="${cfg_dmnames}"
local device
for device in $(echo "${cfg_devices}" | sed 's/,/ /g'); do
local name=$(f_parse_head "${names}" ",")
names=$(f_parse_remove_head "${names}" ",")
[ "${device}" == "${cfg_root_device}" ] && cfg_root_device="/dev/mapper/${name}"
local keyfile="${cfg_tmp_keys_rep}/${index}.key"
if [ -f "${keyfile}" ]; then
f_echo msg "Opening ${name}..."
local gen_prm
if cryptsetup isLuks "${device}" > /dev/null 2>&1; then
gen_prm="luksOpen \"${device}\" \"${name}\""
else
gen_prm="create \"${name}\" \"${device}\""
fi
local stdin_file
local key_prm
if [ "${cfg_util_type}" = "passphrase" ]; then
stdin_file="${keyfile}"
key_prm=""
else
stdin_file="/dev/null"
key_prm="-d \"${keyfile}\""
fi
if ! eval cryptsetup ${key_prm} ${gen_prm} < "${stdin_file}"; then
touch "${cfg_tmp_keys_stat}/${index}.rejected"
f_echo err "Failed to decrypt ${device}"
error=1
fi
fi
index="$((${index}+1))"
done
return "${error}"
}
f_opendevices() {
local error=0
if [ "${cfg_dmcrypt}" != 0 ]; then
f_opendevices_dmcrypt || error=1
elif [ "${cfg_loopaes}" != 0 ]; then
f_opendevices_loopaes || error=1
fi
return "${error}"
}
f_setup_initramfs() {
umask 0077
#
# basic mounts
#
if ! [ -f /proc/cmdline ]; then
mount -t proc none /proc
fi
mount -t sysfs /sys /sys
mount -o remount,rw /
#
# create utilities
#
if [ "${cfg_install_applets}" != 0 ]; then
/bin/busybox --install -s
fi
[ "$0" != "/init" ] && f_die "initrd is not supported"
[ -e /linuxrc -a "$0" = "/init" ] && rm /linuxrc
[ ! -d /tmp ] && mkdir /tmp
[ ! -d "${cfg_remmnt}" ] && mkdir -p "${cfg_remmnt}"
[ -e /dev/tty ] && rm /dev/tty
[ -e /dev/tty0 ] && rm /dev/tty0
ln -s "$(tty)" /dev/tty
ln -s "$(tty)" /dev/tty0
}
f_setup_environment() {
#
# setup path
#
OLDPATH="${PATH}"
export PATH="${PATH}:/sbin:/bin:/usr/sbin:/usr/bin"
#
# parse command line
#
local x
for x in $(cat /proc/cmdline); do
local v="${x#*=}"
case "${x}" in
initrd_suspend_mode=*)
cfg_suspend_mode="${v}"
;;
initrd_util=*)
local t
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_util_type="${t}"
case "${cfg_util_type}" in
"");;
pkcs11)
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_pkcs11_application="${t}"
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_pkcs11_labels="${t}"
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_pkcs11_pass="${t}"
;;
gpgfile)
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_remdev="${t}"
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_remdev_fs="${t}"
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_gpg_files="${t}"
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_gpg_pass="${t}"
;;
passphrase)
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_passphrase_id="${t}"
;;
keyfile)
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_remdev="${t}"
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_remdev_fs="${t}"
t=$(f_parse_head "${v}" ":")
v=$(f_parse_remove_head "${v}" ":")
[ -n "${t}" ] && cfg_keyfile_files="${t}"
;;
*)
f_die "Error: Invalid util argument ${v}"
;;
esac
;;
initrd_devices=*)
cfg_devices="${v}"
;;
initrd_loopstart=*)
cfg_loopstart="${v}"
;;
initrd_dmnames=*)
cfg_dmnames="${v}"
;;
initrd_shell=*)
case "${v}" in
rescue) v=3;;
install) v=5;;
repair) v=10;;
esac
cfg_shell_checkpoint="${v}"
;;
initrd_kmap=*)
cfg_kmap=$(f_parse_head "${v}" ":")
local m=$(f_parse_remove_head "${v}" ":")
[ -n "${m}" ] && cfg_font="${m}"
;;
initrd_encmode=*)
case "${v}" in
loopaes) cfg_loopaes=1;;
loop-aes) cfg_loopaes=1;;
dmcrypt) cfg_dmcrypt=1;;
dm-crypt) cfg_dmcrypt=1;;
esac
;;
initrd_resume_ui)
cfg_resume_ui="${v}"
;;
initrd_readers=*)
echo "${v}" | grep "pcscd" > /dev/null && cfg_pcscd=1
echo "${v}" | grep "openct" > /dev/null && cfg_openct=1
;;
splash=*)
splash_theme=$(echo "${x}" | sed 's/.*theme://' | sed 's/,.*//')
[ -n "$(echo ${x} | grep verbose)" ] && global_splash_verbose=1
;;
root=*)
cfg_root_device="${v}"
;;
rootfstype=*)
cfg_root_type="${v}"
;;
ro)
cfg_root_mode=ro
;;
resume=*)
cfg_resume_device="${v}"
;;
[0123456Ss])
init_arg="${x}"
;;
esac
done
}
main() {
[ ${cfg_setup_initramfs} != 0 ] && f_setup_initramfs
f_setup_environment
f_shell_checkpoint 1
f_modprobe_group "boot"
f_shell_checkpoint 2
[ ${cfg_start_mdev} != 0 ] && f_setup_mdev
f_loadkmap
f_shell_checkpoint 3 # rescue point
if [ -z "${cfg_root_device}" ]; then
f_die "Error: Please specify root kernel parameter at next reboot."
fi
if [ -z "${cfg_util_type}" -a -n "${cfg_devices}" ]; then
f_die "Error: Please specify initrd_util kernel parameter at next reboot."
fi
if [ "${cfg_dmcrypt}" != 0 ]; then
if [ -e "/dev/device-mapper" -a ! -e "/dev/mapper/control" ]; then
[ -d /dev/mapper ] || mkdir /dev/mapper
ln -s /dev/device-mapper /dev/mapper/control
fi
fi
f_shell_checkpoint 5 # install point
if [ -n "${cfg_devices}" ]; then
local success="0"
while [ "${success}" = 0 ]; do
f_shell_checkpoint 6
f_splash_verbose
f_shell_checkpoint 7
f_keys_read
f_splash_silent
f_shell_checkpoint 8
f_opendevices && success="1"
f_shell_checkpoint 9
f_keys_clean
if [ "${cfg_util_type}" != "passphrase" -a "${success}" = 0 ]; then
f_die "Cannot open/decrypt devices"
fi




