This patch adds support for migrating (encrypting) a users home in
place.

Two scenarios are supported.

First: encrypt a home directory. This is the most straightforward
method. The home directory must not be in use, i.e. no file in it is
opened. A lsof check will be done if it's installed. Nor could it be a
mount point. This mode can be used when a user boots from a LiveCD (or
runlevel 1) and can encrypt any home directory on disk.

To do this, run as root:

    $ ecryptfs-migrate-home -e USER


Second: a non-root user can run "ecryptfs-migrate-home -s" to initiate
a migration. The user's home directory will be encrypted on next
reboot. By this a non-root user can opt to encrypt the home directory
when the system is already deployed. Now a home-encryption-tag-file is
created in the user's home.

To enable this feature, a sysop should modify a boot-up script to run
"ecryptfs-migrate-home -b" on each boot, which will do nothing if no
home-encryption-tag-file is found. This should be done before the user
login attempt.

In this mode, a mount password will be generated randomly, and stored
in /dev/shm temporarily.

When the "ecryptfs-migrate-home -b" finishes, and the user's home
encrypted successfully, the system proceeds to login. After the user
types in the correct login password, the pam_ecryptfs will wrap the
mount password by using the user's login password and the temporary
file holding the mount password is removed.

And the migration is done.

Files modified:

  src/pam_ecryptfs/pam_ecryptfs.c

    Wrapping passphrase file when needed during authentication, this
    was only done when changing a password.

    Also removed checking for wrapped passphrase file in
    ecryptfs_pam_automount_set(), since checking .ecryptfs/auto-mount
    file is enough and the wrapped passphrase file is not created
    before we wrapped it after an user-initiated migration.

  src/utils/ecryptfs-migrate-home

    The new script that does the migration work.

  src/utils/Makefile.am

    To include ecryptfs-migrate-home


TODO:

  1. free space check before migration

  2. prompt the user to remove the /home/USER.old after he checks and
     is sure that the migration is successful

---
diff -Nur ecryptfs-utils-82.orig/src/pam_ecryptfs/pam_ecryptfs.c 
ecryptfs-utils-82/src/pam_ecryptfs/pam_ecryptfs.c
--- ecryptfs-utils-82.orig/src/pam_ecryptfs/pam_ecryptfs.c      2009-10-21 
02:49:55.000000000 +0800
+++ ecryptfs-utils-82/src/pam_ecryptfs/pam_ecryptfs.c   2010-01-19 
11:39:29.000000000 +0800
@@ -1,4 +1,5 @@
-/**
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
+ *
  * pam_ecryptfs.c: PAM module that sends the user's authentication
  * tokens into the kernel keyring.
  *
@@ -73,17 +74,6 @@
        char *file_path;
        int rc = 0;
        struct stat s;
-       if (asprintf(
-               &file_path, "%s/.ecryptfs/%s",
-               homedir,
-               ECRYPTFS_DEFAULT_WRAPPED_PASSPHRASE_FILENAME) == -1)
-               return -ENOMEM;
-       if (stat(file_path, &s) != 0) {
-               if (errno != ENOENT)
-                       rc = -errno;
-               goto out;
-       }
-       free(file_path);
        if (asprintf(&file_path, "%s/.ecryptfs/auto-mount", homedir) == -1)
                return -ENOMEM;
        if (stat(file_path, &s) != 0) {
@@ -133,10 +123,13 @@
        if (!ecryptfs_pam_automount_set(homedir))
                goto out;
        private_mnt = ecryptfs_fetch_private_mnt(homedir);
-       if (ecryptfs_private_is_mounted(NULL, private_mnt, NULL, 1))
+       if (ecryptfs_private_is_mounted(NULL, private_mnt, NULL, 1)) {
+               syslog(LOG_INFO, "%s: %s is already mounted\n", __FUNCTION__,
+                      homedir);
                /* If private/home is already mounted, then we can skip
                   costly loading of keys */
                goto out;
+       }
        /* we need side effect of this check:
           load ecryptfs module if not loaded already */
        if (ecryptfs_get_version(&version) != 0)
@@ -186,6 +179,33 @@
                                rc = -ENOMEM;
                                goto out_child;
                        }
+
+                       /* If /dev/shm/.ecryptfs-$USER exists and owned by the 
user
+                          and ~/.ecryptfs/wrapped-passphrase does not exist
+                          and a new_passphrase is set:
+                          wrap the unwrapped passphrase file */
+                       char *unwrapped_pw_filename;
+                       struct stat s;
+                       rc = asprintf(&unwrapped_pw_filename, 
"/dev/shm/.ecryptfs-%s", username);
+                       if (rc == -1) {
+                               syslog(LOG_ERR, "Unable to allocate memory\n");
+                               rc = -ENOMEM;
+                               goto out_child;
+                       }
+                       if (stat(unwrapped_pw_filename, &s) == 0 && (s.st_uid 
== uid) &&
+                               stat(wrapped_pw_filename, &s) != 0      &&
+                               passphrase != NULL && *passphrase != '\0') {
+                               syslog(LOG_INFO, "Founded unwrapped passphrase 
file, attempt to wrap");
+                               rc = 
ecryptfs_wrap_passphrase_file(wrapped_pw_filename,
+                                                                               
                   passphrase, salt, unwrapped_pw_filename);
+                               if (rc != 0) {
+                                       syslog(LOG_ERR, "Error wrapping 
cleartext password; "
+                                                  "rc = [%d]\n", rc);
+                                       goto out_child;
+                               }
+                               syslog(LOG_INFO, "Passphrase file wrapped");
+                       }
+
                        rc = ecryptfs_insert_wrapped_passphrase_into_keyring(
                                auth_tok_sig, wrapped_pw_filename, passphrase,
                                salt);
diff -Nur ecryptfs-utils-82.orig/src/utils/ecryptfs-migrate-home 
ecryptfs-utils-82/src/utils/ecryptfs-migrate-home
--- ecryptfs-utils-82.orig/src/utils/ecryptfs-migrate-home      1970-01-01 
08:00:00.000000000 +0800
+++ ecryptfs-utils-82/src/utils/ecryptfs-migrate-home   2010-01-15 
17:04:44.000000000 +0800
@@ -0,0 +1,310 @@
+#!/bin/sh
+# -*- sh-basic-offset: 4; sh-indentation: 4; tab-width: 4; indent-tabs-mode: 
t; sh-indent-comment: t; -*-
+# This script encrypts an user's home
+#
+# Written by Yan Li <[email protected]>, <[email protected]>
+# Copyright (C) 2010 Intel Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+set -e -u
+
+# consts
+# ENCRYPT_TAG_FILE_NAME can't contain spaces
+ENCRYPT_TAG_FILE_NAME=.ENCRYPT_ME_ON_NEXT_BOOT
+PRIVATE_DIR="Private"
+
+usage() {
+       echo "
+This script encrypts an user's data in home in place.
+
+There are two ways to use this script:
+
+ a. you can run this script as root, to encrypt any directory by using
+    the -e option. You have to make sure that directory is not in use
+    (the script will also check for this)
+
+ b. if your distro supports this, you can also run this script a a
+    normal user with -s (or --setup) to setup encryption to run on
+    next reboot, and your home will be automatically encrypted on next
+    boot. If your distro didn't support this, running with -s is no
+    use
+
+WARNING: Make a complete backup copy of your non-encrypted data to
+another system or external media. This script is dangerous and, in
+case of an error, could result in data lost, or lock you out of your
+system!
+
+Usage:
+
+$0 -s   or
+$0 -b   or
+$0 -e USER [-l|--loginpass LOGINPASS] [-m|--mountpass MOUNTPASS]
+
+ -e, --encrypt    Encrypt USER's home in place
+ -s, --setup      Set up encryption for current user to run on next boot
+ -l, --loginpass  Login/Wrapping passphrase for USER,
+                  used to wrap MOUNTPASS
+ -m, --mountpass  Passphrase for mounting the ecryptfs directory,
+                  defaults to randomly generated
+ -v, --verbose    Output debug messages
+ -b, --boot       Boot mode. Use this option on boot to do encryption on
+                  demand. SHOULD ONLY BE CALLED from a boot script. You
+                  should NOT use this option directly
+
+"
+       exit 1
+}
+
+error() {
+       echo "$(gettext 'ERROR: ')" "$@" 1>&2
+       exit 1
+}
+
+warning() {
+       echo "$(gettext 'WARNING: ')" "$@" 1>&2
+}
+
+debug() {
+       if [ $VERBOSE -eq 1 ]; then
+               echo "$(gettext 'DEBUG: ')" "$@" 1>&2
+       fi
+}
+
+assert_dir_empty() {
+       # arguments:
+       local DIR=${1}
+
+       if [ -e "${DIR}" ]; then
+               # if ${DIR} is a directory, make sure it's empty
+               if [ -d "${DIR}" ]; then
+                       if [ `ls -A "${DIR}" | wc -l` -ne 0 ]; then
+                               echo 1>&2 "If you already have some data in 
directory ${DIR},"
+                               echo 1>&2 "please move all of these files and 
directories out of the way, and"
+                               echo 1>&2 "follow the instructions in:"
+                               echo 1>&2 "    ecryptfs-setup-private --undo"
+                               echo 1>&2 
+                               error "${DIR} is not empty, can't continue"
+                       fi
+               else
+                       error "${DIR} exists but is not an empty directory, 
can't continue"
+               fi
+       fi
+}
+
+# get user home by id
+get_user_home () {
+       local USER_ID=$1
+
+       local USER_HOME=`grep "^${USER_ID}:" /etc/passwd | cut -d":" -f 6`
+       if [ -z "$USER_HOME" ]; then
+               error "can't find user home for $USER_ID"
+       fi
+
+       echo $USER_HOME
+}
+
+# get user id by home
+get_user_id () {
+       local USER_HOME=$1
+
+       local USER_ID=`grep -F ":${USER_HOME}:" /etc/passwd | cut -d":" -f 1`
+       if [ -z "$USER_ID" ]; then
+               error "can't find user home for $USER_ID"
+       fi
+
+       echo $USER_ID
+}
+
+sanity_check () {
+       # arguments:
+       # remove trailing "/"
+       local USER_ID=${1}
+       local USER_HOME=${2%/}
+
+       # Is $USER_HOME a mountpoint?
+       if mount | grep -q -F " ${USER_HOME} "; then
+               error "${USER_HOME} is a mountpoint or it's already encrypted, 
we can't migrate it for now"
+       fi
+
+       # Check for rsync
+       if ! which rsync >/dev/null 2>&1; then
+               error "can't find rsync, please install the rsync package"
+       fi
+
+       # Check free space: make sure we have sufficient disk space
+       # available. To make a full copy, we will need at least 2x the
+       # disk usage of the target home directory.
+
+       # TODO: not implemented yet
+       # if DO_NOT_HAVE_ENOUGH_SPACE; then
+       #       error "not enough free space, I need at least 2x the disk usage 
of your current home directory"
+       # fi
+
+       # Check directories
+       assert_dir_empty "${USER_HOME}.old" && rm -rf "${USER_HOME}.old"
+       assert_dir_empty "${USER_HOME}/.${PRIVATE_DIR}"
+       assert_dir_empty "${USER_HOME}/.ecryptfs"
+       assert_dir_empty "/home/.ecryptfs/${USER_ID}"
+}
+
+encrypt_dir ()
+{
+       # argument:
+       # remove trailing "/"
+       local USER_ID=$1
+       local USER_HOME=${2%/}
+       local LOGINPASS=${3:-}
+       local MOUNTPASS=${4:-}
+
+       # check whether USER_HOME is in use
+       if ! which lsof >/dev/null 2>&1; then
+               warning "lsof not found, I don't know whether your home is in 
use or not"
+       else
+               debug "checking for open files in $USER_HOME"
+               if [ `lsof +D "$USER_HOME" | wc -l` -ne 0 ]; then
+                       lsof +D "$USER_HOME"
+                       echo
+                       error "user ${USER_ID} has opened the files above, 
can't proceed"
+               fi
+       fi
+
+       # start encryption
+       mv "${USER_HOME}" "${USER_HOME}.old"
+       mkdir -p -m 700 "${USER_HOME}"
+       chown -R ${USER_ID}.${USER_ID} "${USER_HOME}"
+       ECRYPTFS_SETUP_PRIVATE_ARGS=""
+       if [ -n "$LOGINPASS" ]; then
+               ECRYPTFS_SETUP_PRIVATE_ARGS="-l ${LOGINPASS}"
+       fi
+       if [ -n "$MOUNTPASS" ]; then
+               ECRYPTFS_SETUP_PRIVATE_ARGS="$ECRYPTFS_SETUP_PRIVATE_ARGS -m 
${MOUNTPASS}"
+       fi
+       if ! ecryptfs-setup-private -u "$USER_ID" -b 
$ECRYPTFS_SETUP_PRIVATE_ARGS; then
+               # too bad, something went wrong, we'll try to recover
+               rm -rf "${USER_HOME}"
+               mv "${USER_HOME}.old" "${USER_HOME}"
+               exit 1
+       fi
+       debug "encrypt home has been set up, encrypting files now..."
+       rsync -a "${USER_HOME}.old/" "${USER_HOME}/"
+       umount "${USER_HOME}/"
+}
+
+DO_SETUP=0
+DO_BOOT=0
+DO_ENCRYPT=0
+VERBOSE=0
+while [ ! -z "${1:-}" ]; do
+       case "$1" in
+               -u|--username)
+                       USER="$2"
+                       shift 2
+                       ;;
+               -l|--loginpass)
+                       LOGINPASS="$2"
+                       shift 2
+                       ;;
+               -m|--mountpass)
+                       MOUNTPASS="$2"
+                       shift 2
+                       ;;
+               -w|--wrapping)
+                       WRAPPING_PASS="INDEPENDENT"
+                       MESSAGE="$(gettext 'Enter your wrapping passphrase')"
+                       shift 1
+                       ;;
+               -s|--setup)
+                       DO_SETUP=1
+                       shift 1
+                       ;;
+               -b|--boot)
+                       DO_BOOT=1
+                       shift 1
+                       ;;
+               -v|--verbose)
+                       VERBOSE=1
+                       shift 1
+                       ;;
+               -e|--encrypt)
+                       DO_ENCRYPT=1
+                       USER_ID=$2
+                       USER_HOME=`get_user_home ${USER_ID}`
+                       shift 2
+                       ;;
+               *)
+                       usage
+                       ;;
+       esac
+done
+
+if [ `expr $DO_SETUP + $DO_BOOT + $DO_ENCRYPT` -ne 1 ]; then
+       usage
+fi
+
+if [ $DO_SETUP -eq 1 ]; then
+       # we can't encrypt root's home
+       if [ `id -u` -eq 0 ]; then
+               error "can't encrypt root's home, you should use setup as a 
normal user"
+       fi
+
+       # LOGINPASS and MOUNTPASS will just be ignored here
+       if [ -n "${LOGINPASS:-}" -o -n "${MOUNTPASS:-}" ]; then
+               warning "Setting up encryption on next boot, and 
LOGINPASS/MOUNTPASS will just be ignored. A random MOUNTPASS will be generated 
automatically"
+       fi
+
+       sanity_check $HOME
+
+       # set up a tag
+       touch ~/${ENCRYPT_TAG_FILE_NAME}
+
+       echo "I have set the encryption tag, now you should reboot"
+       exit 0
+fi
+
+if [ $DO_BOOT -eq 1 ]; then
+       # I must be run as root now
+       if [ `id -u` -ne 0 ]; then
+               error "boot mode can only be used as root"
+       fi
+
+       # search for ENCRYPT_TAG_FILE_NAME, FIXME: now we handle the first
+       # one only fow now
+       USER_HOME=`ls /home/*/${ENCRYPT_TAG_FILE_NAME} 2>/dev/null | head -1`
+       USER_HOME=${USER_HOME%/*}
+       if [ -z "$USER_HOME" ]; then
+               debug "no encrypt tag file found, bye"
+               exit 0
+       fi
+       USER_ID=`get_user_id "${USER_HOME}"`
+
+       rm -f "${USER_HOME}/${ENCRYPT_TAG_FILE_NAME}"
+
+       # check for ecryptfs kernel module
+       lsmod | grep -q ecryptfs || {
+               modprobe ecryptfs
+       }       
+       
+       sanity_check "$USER_ID" "$USER_HOME"
+       encrypt_dir "$USER_ID" "$USER_HOME"
+       exit 0
+fi
+
+if [ $DO_ENCRYPT -eq 1 ]; then
+       sanity_check "$USER_ID" "$USER_HOME"
+       encrypt_dir "$USER_ID" "$USER_HOME" "${LOGINPASS:-}" "${MOUNTPASS:-}"
+       exit 0
+fi
diff -Nur ecryptfs-utils-82.orig/src/utils/Makefile.am 
ecryptfs-utils-82/src/utils/Makefile.am
--- ecryptfs-utils-82.orig/src/utils/Makefile.am        2009-10-21 
02:49:55.000000000 +0800
+++ ecryptfs-utils-82/src/utils/Makefile.am     2010-01-19 10:59:18.000000000 
+0800
@@ -1,6 +1,6 @@
 MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
 
-EXTRA_DIST=ecryptfsrc ecryptfs-rewrite-file ecryptfs-setup-private 
ecryptfs-setup-swap ecryptfs-mount-private ecryptfs-umount-private
+EXTRA_DIST=ecryptfsrc ecryptfs-rewrite-file ecryptfs-setup-private 
ecryptfs-setup-swap ecryptfs-mount-private ecryptfs-umount-private 
ecryptfs-migrate-home
 
 rootsbin_PROGRAMS=mount.ecryptfs \
                  umount.ecryptfs \
@@ -15,7 +15,8 @@
              ecryptfs-setup-swap \
              ecryptfs-mount-private \
              ecryptfs-umount-private \
-             ecryptfs-rewrite-file
+             ecryptfs-rewrite-file \
+             ecryptfs-migrate-home
 bin2dir = $(bindir)
 
 noinst_PROGRAMS=test

-- 
Best regards,
Li, Yan

Moblin Team, Opensource Technology Center, SSG, Intel
Office tel.: +86-10-82171695 (inet: 8-758-1695)
OpenPGP key: 5C6C31EF
IRC: yanli on network irc.freenode.net

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Mailing list: https://launchpad.net/~ecryptfs-users
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~ecryptfs-users
More help   : https://help.launchpad.net/ListHelp

Reply via email to