ecryptfs-devel team mailing list archive
-
ecryptfs-devel team
-
Mailing list archive
-
Message #00113
[PATCH] Support migrating an user's home
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 <yan.i.li@xxxxxxxxx>, <yanli@xxxxxxxxx>
+# 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
Follow ups