Linux login and encrypted directory

The traditional method for this is to hash the password you entered when logging in and compare it with the password in /etc/shadow and also use that password to decrypt a directory or partition.

PAM-script, EncFS and TPM

PAM script can simplify this: you will decrypt the directory with the logon password (using EncFS), and if that works, you will be logged in immediately. /etc/shadow is no longer needed.

To prevent brute force attacks on the (disassembled) hard disk, the password must be good. Since a long password is hard to remember, you can keep it safe with TPM. A short password is sufficient for this as the TPM hardware prevents brute force attacks.

With the PAM module PAM-script you can control the Linux login with a shell script.The following script makes a login dependent on the successful decryption of a directory encrypted with EncFs. The EncFs password can be sealed using TPM2.

The following configuration is tested for Debian Buster with the MATE Desktop.

Using EncFS

Debian packages needed: encfs, libpam-script

At first login any password can be used. With this password the EncFS encrypted directory .private with the “plain”-directory private will be created.

At next login .private will be decrypted by the given login password to private. If decryption succeeds, the login succeeds. The old password in /etc/shadow can then be deleted.

PAM-script

as root:

cd /usr/share/libpam-script/
for i in auth passwd ses_close ses_open; do
  ln -s /etc/kalinx/pam_script_encfs pam_script_$i
done
mkdir -p /etc/kalinx

/etc/kalinx/pam_script_encfs

#!/bin/sh

test "$PAM_USER" = root && exit 1

log() {
  out=$ka/log
  test $id != 0 && out=/run/user/$id/kalinx.log
  echo $1 >>$out
}

ka=/run/kalinx
id=$(id -ur)
name=$(basename $0)

#----------------------------------------------

if test $name = pam_script_auth; then

  log "AUTH $PAM_USER - $(date) - $id - $PAM_SERVICE"

  if test -e $ka/md_$PAM_USER; then
    # screensaver or another login
    read s md x <$ka/md_$PAM_USER
    mdx=$(echo "$s$PAM_AUTHTOK" | md5sum | cut -c1-32) 
    test $mdx = "$md" && exit 0
    exit 1
  fi

  test $id != 0 && exit 1
  mkdir -p $ka/

  id -u "$PAM_USER" >/dev/zero 2>&1 || exit 1
  hm=$(eval echo ~$PAM_USER)
  runuser $PAM_USER -c "cd;mkdir -p private .private"

  encpw=$PAM_AUTHTOK
  if test -f $hm/.private/.tpm2; then
    hd=$(cat $hm/.private/.tpm2)
    if tpm2_listpersistent | grep ":$hd "; then
      pw=$(tpm2_unseal -H "$hd" -P "$PAM_AUTHTOK")
      if test "$pw"; then
        log "TPM unsealed encfs password"
        encpw=$pw
        x=tpm
      else
        log "TPM unseal failed"
      fi
    fi
  fi

  echo "$encpw" | 
    runuser $PAM_USER -c "encfs -S --standard $hm/.private/ $hm/private"
  if test -r $hm/private; then
    log "encfs mount failed"
    exit 1
  fi

  echo 0 > $ka/cnt_$PAM_USER
  s=$(dd if=/dev/urandom count=8 bs=1 2>/dev/null | base64)
  md=$(echo "$s$PAM_AUTHTOK" | md5sum | cut -c1-32)
  umask 077
  echo $s $md $x>$ka/md_$PAM_USER
  chown $PAM_USER $ka/md_$PAM_USER

#----------------------------------------------

elif test $name = pam_script_passwd; then

  log "PASSWD $PAM_USER - $(date) - $id - $PAM_SERVICE"
  test $id = 0 && exit 1

  read s md x <$ka/md_$PAM_USER
  hm=$(eval echo ~$PAM_USER)

  if test "$x" = tpm; then
    log "TPM2 password change ist not implemented yet"
    exit 1
  else
    printf "%s\n%s\n" "$PAM_OLDAUTHTOK" "$PAM_AUTHTOK" | 
      encfsctl autopasswd $hm/.private
    test $? != 0 && exit 1
  fi

  s=$(dd if=/dev/urandom count=8 bs=1 2>/dev/null | base64)
  md=$(echo "$s$PAM_AUTHTOK" | md5sum | cut -c1-32)
  umask 077
  echo $s $md $x >/run/kalinx/md_$PAM_USER

#----------------------------------------------

elif test $name = pam_script_ses_open; then

  test $PAM_SERVICE = systemd-user && exit 1
  test -e /run/kalinx/cnt_$PAM_USER || exit 0
  log "OPEN $PAM_USER - $(date) - $id - $PAM_SERVICE"

  n=$(cat /run/kalinx/cnt_$PAM_USER)
  echo $(($n+1)) > /run/kalinx/cnt_$PAM_USER

elif test $name = pam_script_ses_close; then

  test -e /run/kalinx/cnt_$PAM_USER || exit 0
  log "CLOSE $PAM_USER - $(date) -  $id - $PAM_SERVICE"

  n=$(($(cat /run/kalinx/cnt_$PAM_USER) - 1))
  echo $n > /run/kalinx/cnt_$PAM_USER
  if test $n = 0; then
    hm=$(eval echo ~$PAM_USER)
    fusermount -u $hm/private
    rm /run/kalinx/*_$PAM_USER
    log "CLOSE cleaned"
  fi
fi

exit 0
chmod +x /etc/kalinx/pam_script_encfs

Optional TPM2 encryption of the EncFS password

Debian package needed: tpm2-tools

We use TPM to keep the long EncFs password safe. For TPM a short handy password is sufficient, because TPM protects against bruteforce and dictionary attacks.

as root:

tpm2_createprimary -H o -g sha1 -G rsa -C /tmp/prim.ctx
tpm2_create -g sha256 -G keyedhash -u /tmp/o.pub -r /tmp/o.priv -c /tmp/prim.ctx -I - -K password
long_and_good_encfs_password
<Ctrl>D

tpm2_load -c /tmp/prim.ctx -u /tmp/o.pub -r /tmp/o.priv -C /tmp/load.ctx
tpm2_evictcontrol -A o -c /tmp/load.ctx -S 0x81000019

tpm2_unseal -H 0x81000019 -P password    # test decrypt
history -c ; history -r                  # clear history

as regular user:

$ cd
$ mkdir -p .private
$ echo 0x81000019 >.private/.tpm2

You can then log in using either the EncFs password or the TPM2 password. The passwd command can only change the EncFS password.

Changes for TPM2 4.0 (Debian Bullseye)

# tpm2_createprimary -c /tmp/prim.ctx
# tpm2_create -C /tmp/prim.ctx  -i - -p passw -u /tmp/o.publ -r /tmp/o.priv 
long_and_good_encfs_password
<Ctrl>D
# tpm2_load -C /tmp/prim.ctx  -r /tmp/o.priv -u /tmp/o.publ -c /tmp/load.ctx
# tpm2_evictcontrol  -c /tmp/load.ctx  0x81000019

# tpm2_unseal -p passw -c 0x81000019

/etc/kalinx/pam_script_encfs

        .
        hd=$(cat $hm/.private/.tpm2)
        if tpm2_getcap handles-persistent | grep -- "^- $hd$"; then
            pw=$(tpm2_unseal -c "$hd" -p "$PAM_AUTHTOK")
        .

What you can do with it

cd
mv .ssh/id_rsa private
ln -s ../private/id_rsa .ssh/
mv .mozilla/firefox/*.default/key4.db private
ln -s ../../../../private/key4.db .mozilla/firefox/*.default/

References

PAM-script

EncFS

tpm2-tools


easylang.online/blog


easylang.online - easy programming online