Automatic encryption of home directories using TrueCrypt 6.2a and pam_exec

This post describes how to encrypt the home directory of your users on GNU Linux with the help of TrueCrypt and PAM using the login-password as encryption key.
I wrote about Automatic encryption of home directories using TrueCrypt before. This time we’ll use TrueCrypt 6.2a. Futhermore we’ll use Ubuntu 9.04 Jaunty Jackalope and we replaced pam_mount by pam_exec. For convenience this post will be selfcontained (ie. I copy redundant parts from the old one).


Using the method described below is no silver bullet and has some issues:

  • Your user passwords may be weak.
  • If your computer gets stolen while turned on, the passwort may be easier to recover than you might think (see here).
  • Changing the password requires to log in as root.
  • Some programs (e.g. qmail) rely on an accessible home directory.
  • The home may accidently stay mounted after logout until the next reboot.
  • Suspend to ram and suspend to disk are not treated carefully enough.
  • There may be glitches in my scripts.

Prequisites

Setup

We will create one file for each user to hold his encrypted home directory. To keep them we create a directory:

mkdir /home/private

For each user we have to create an encrypted file in /home/private. We start with the user bart (if you leave out the --text you’ll get a graphical user interface).

root@mybox:~# truecrypt  --text --create /home/private/bart.tc
Volume type:
1) Normal
2) Hidden
Select [1]: 1

Enter volume size (bytes - size/sizeK/sizeM/sizeG): 1G

Encryption algorithm:
 1) AES
 2) Serpent
 3) Twofish
 4) AES-Twofish
 5) AES-Twofish-Serpent
 6) Serpent-AES
 7) Serpent-Twofish-AES
 8 ) Twofish-Serpent
Select [1]: 1

Hash algorithm:
 1) RIPEMD-160
 2) SHA-512
 3) Whirlpool
Select [1]: 1

Filesystem:
 1) FAT
 2) None
Select [1]: 2

Enter password: *************
WARNING: Short passwords are easy to crack using brute force techniques!

We recommend choosing a password consisting of more than 20 characters. Are you sure you want to use a short password? (y=Yes/n=No) [No]: y

Re-enter password:*************

Enter keyfile path [none]:

Please type at least 320 randomly chosen characters and then press Enter:
dsglregmm;adsf;dsafdsasasadfdsafdsagfdsadsafdsafdsafadsfdsahfarweqasddsaglfdsakg;lrewqk;lggkqqqqqewrgsadgdsag....

Done: 100.000%  Speed: 18.0 MB/s  Left: 0 s

The TrueCrypt volume has been successfully created.

Then we assign the same password as the login password to bart:

root@mybox:~# passwd bart
Enter new UNIX password:
Retype new UNIX password:

Next, we need to format the encrypted partition and move the old home directory into it:

root@mybox:~# truecrypt --text --filesystem=none /home/private/bart.tc
Enter password for /home/hgerlach/newcrypt.tc: *************
Enter keyfile [none]:
Protect hidden volume? (y=Yes/n=No) [No]:

root@mybox:~#  truecrypt --text -l
1: /home/private/bart.tc /dev/mapper/truecrypt1 -

root@mybox:~# mkfs.ext2 /dev/mapper/truecrypt1
root@mybox:~# mount /dev/mapper/truecrypt1 /mnt/
root@mybox:~# shopt -s dotglob #make dotfiles visible
root@mybox:~# mv /home/bart/* /mnt/
root@mybox:~# chown bart.users /mnt/
root@mybox:~# umount /mnt
root@mybox:~# truecrypt -d /dev/mapper/truecrypt1

Now we have to make sure the homedir gets mounted on login. Previously I used pam_mount but they dropped truecrypt support and sound somewhat angry on trucrypt (because they changed their interface). I was not so happy with pam_mount myself and reported a bug which got fixed quickly but will take some time to appear downstream in Ubuntu or Debian.

This time I decided to roll my own mount/umount script. It relies on a “modern” version of pam_exec that supports the expose_authok option. Unfortunally Ubuntu does not come with this modern version yet, so you either have to download it from me (i386-32bit, no guarantees, infected with rootkit by now) or compile it yourself. You should copy the lib to
/lib/security/pam_exec_UNSTABLE.so so pam can find it.

To the file ‘/etc/pam.d/common-auth’ we add the line:
auth optional pam_exec_UNSTABLE.so debug expose_authtok seteuid /bin/bash /bin/cryptmount.sh
so it looks like

# /etc/pam.d/common-auth
# here are the per-package modules (the "Primary" block)
auth    [success=1 default=ignore]      pam_unix.so nullok_secure
# here's the fallback if no module succeeds
auth    requisite                       pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth    required                        pam_permit.so
# and here are more per-package modules (the "Additional" block)
# end of pam-auth-update config
auth    optional     pam_exec_UNSTABLE.so  debug expose_authtok seteuid /bin/bash /bin/cryptmount.sh

and to ‘/etc/pam.d/common-session’ we add the line
session optional pam_exec_UNSTABLE.so seteuid /bin/bash /bin/cryptmount.sh
so it looks like

# ...
# here are the per-package modules (the "Primary" block)
session [default=1]                     pam_permit.so
# here's the fallback if no module succeeds
session requisite                       pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
session required                        pam_permit.so
# and here are more per-package modules (the "Additional" block)
session required        pam_unix.so 
session optional                        pam_ck_connector.so nox11
# end of pam-auth-update config
session optional     pam_exec_UNSTABLE.so seteuid /bin/bash /bin/cryptmount.sh

Everytime we log in or a new session starts the script /bin/cryptmount.sh gets executed.
Thats what I propose it to look like:

#!/bin/bash
# /bin/cryptmount.sh

CRYPTVOLUME=/home/private/$PAM_USER.tc
MOUNTPOINT=/home/$PAM_USER

case "$PAM_USER" in
  root | lisa ) #homedirs of root and lisa are not encrypted
    exit 0
  ;;
esac

case "$PAM_TYPE" in
      auth )
         head -c -1 | truecrypt -t  --protect-hidden=no -k "" \
                      "$CRYPTVOLUME" "$MOUNTPOINT"
      ;;
      close_session ) 
         MOUNTS=$(mount | grep " $MOUNTPOINT ") 
         if test -z $MOUNTS ; then
           echo MOUNTS  $MOUNTS > /tmp/debug
           exit 0
         fi
         OTHER=$(who | grep "^$PAM_USER " | grep -v " $PAM_TTY ")
         if test -z "$OTHER"; then
            echo truecrypt -d $MOUNTPOINT | at now + 1 minute
         fi
      ;;
esac
exit 0

It first checks whether root or lisa (adapt to your own needs) is calling it. Both of them do not have encrypted homedirs, so the script exits. In the second case clause it checks wheter it is called while authenticating and mounts the homdir using the login passphrase or if it is called for closing a session. In the latter case it schedules unmounting the homedir in one minute.

Now bart can login and use the encrypted home. One minute after logoff the homedir will be umounted and encrypted again.

How to roll your own pam_exec_UNSTABLE.so

# get the source (after searching packages.debian.org)
wget http://ftp.de.debian.org/debian/pool/main/p/pam/pam_1.1.0.orig.tar.gz
tar xfzv pam_1.1.0.orig.tar.gz
cd Linux-PAM-1.1.0/
./configure

# install all packages needed for configure to run through
cd modules/pam_exec/
make
# yes this is dirty, but who cares.
sudo cp .libs/pam_exec.so /lib/security/pam_exec_UNSTABLE.so