2 # This script sets up an ecryptfs mount in a user's ~/Private
4 # Originally ecryptfs-setup-pam-wrapped.sh by Michael Halcrow, IBM
6 # Ported for use on Ubuntu by Dustin Kirkland <kirkland@ubuntu.com>
7 # Copyright (C) 2008 Canonical Ltd.
8 # Copyright (C) 2007-2008 International Business Machines
11 ECRYPTFS_DIR="/home/.ecryptfs"
13 TEXTDOMAIN="ecryptfs-utils"
14 MESSAGE="$(gettext 'Enter your login passphrase')"
19 # Zero out user-defined GREP_OPTIONS, such as --line-number
26 $0 [-f|--force] [-w|--wrapping] [--nopwcheck] [-n|--no-fnek]
27 [-u|--username USER] [-l|--loginpass LOGINPASS]
28 [-m|--mountpass MOUNTPASS]
30 -f, --force Force overwriting of an existing setup
31 -w, --wrapping Use an independent wrapping passphrase,
32 different from the login passphrase
33 -n, --no-fnek Do not encrypt filenames; If this flag is
34 omitted, and the kernel supports filename
35 encryption, then filenames will be encrypted
36 -u, --username Username for encrypted private mountpoint,
38 -l, --loginpass Login/Wrapping passphrase for USER,
39 used to wrap MOUNTPASS
40 --nopwcheck Do not check the validity of the specified
41 login password (useful for LDAP user accounts)
42 --noautomount Setup this user such that the encrypted private
43 directory is not automatically mounted on login
44 --noautoumount Setup this user such that the encrypted private
45 directory is not automatically unmounted at
47 -m, --mountpass Passphrase for mounting the ecryptfs directory,
48 defaults to randomly generated $KEYBYTES bytes
49 -b, --bootstrap Bootstrap a new user's entire home directory
50 Generates a random mount passphrase, which
51 will be wrapped when the new login passphrase
52 is set. SHOULD ONLY BE CALLED FROM 'adduser'.
53 --undo Provide instructions on how to undo an
54 encrypted private setup
56 Be sure to properly escape your parameters according to your
57 shell's special character nuances, and also surround the
58 parameters by double quotes, if necessary.
65 In the event that you want to remove your eCryptfs Private Directory setup,
66 you will need to very carefully perform the following actions manually:
68 1. Obtain your Private directory mountpoint
69 $ PRIVATE=\`cat ~/.ecryptfs/Private.mnt 2>/dev/null || echo \$HOME/$PRIVATE_DIR\`
70 2. Ensure that you have moved all relevant data out of your \$PRIVATE directory
71 3. Unmount your encrypted private directory
72 $ ecryptfs-umount-private
73 4. Make your Private directory writable again
75 5. Remove \$PRIVATE, ~/.Private, ~/.ecryptfs
76 Note: THIS IS VERY PERMANENT, BE VERY CAREFUL
77 $ rm -rf \$PRIVATE ~/.Private ~/.ecryptfs
78 6. Uninstall the utilities (this is specific to your Linux distribution)
79 $ sudo apt-get remove ecryptfs-utils libecryptfs0
84 echo "$(gettext 'ERROR: ')" "$@" 1>&2
91 /sbin/umount.ecryptfs_private >/dev/null
96 random_passphrase () {
98 # Pull $1 of random data from /dev/random,
99 # and convert to a string of hex digits
100 od -x -N $bytes --width=$bytes /dev/random | head -n 1 | sed "s/^0000000//" | sed "s/\s*//g"
103 filename_encryption_available() {
104 version=$(cat /sys/fs/ecryptfs/version 2>/dev/null)
105 [ -z "$version" ] && error "$(gettext 'Cannot get ecryptfs version, ecryptfs kernel module not loaded?')"
106 [ $(($version & 0x100)) -eq 0 ] && return 1
110 filename_encryption_available && FNEK="--fnek"
112 if [ ! -z "$SUDO_USER" ]; then
116 while [ ! -z "$1" ]; do
131 WRAPPING_PASS="INDEPENDENT"
132 MESSAGE="$(gettext 'Enter your wrapping passphrase')"
156 [ `whoami` = "root" ] || error "$(gettext 'You must be root to bootstrap encrypt a home directory')"
158 MOUNTPASS=`random_passphrase $KEYBYTES`
172 # Prompt for the USER name, if not on the command line and not in the env
173 if [ -z "$USER" ]; then
175 echo -n "$(gettext 'Enter the username: ')"
178 if [ -z "$USER" ]; then
179 echo "$(gettext 'ERROR: ')" "$(gettext 'You must provide a username')"
182 # Verify that the user exists
183 if ! id "$USER" >/dev/null; then
184 echo "$(gettext 'ERROR: ')" "$(gettext 'User does not exist')" " [$USER]"
191 # Verify that the user exists
192 id "$USER" >/dev/null || error "$(gettext 'User does not exist')" "[$USER]"
195 # Obtain USER's primary group
198 # Check if the ecryptfs group exists, and user is member of ecryptfs group
199 if grep -qs "^ecryptfs:" /etc/group; then
200 if ! id "$USER" | grep -qs "\(ecryptfs\)"; then
201 error "$(gettext 'User needs to be a member of ecryptfs group')"
205 # Obtain the user's home directory
206 HOME=`getent passwd "$USER" | awk -F: '{print $6}'`
207 if [ ! -d "$HOME" ]; then
208 error "$(gettext 'User home directory does not exist')" "[$HOME]"
211 if [ "$BOOTSTRAP" = "1" ]; then
212 # If we want to encrypt the entire homedir, we need the .ecryptfs
213 # config dir elsewhere, but linked into the homedir
214 mkdir -p -m 700 $ECRYPTFS_DIR/$USER/.ecryptfs
215 ln -sf $ECRYPTFS_DIR/$USER/.ecryptfs $HOME/.ecryptfs
216 ln -sf $ECRYPTFS_DIR/$USER/.$PRIVATE_DIR $HOME/.$PRIVATE_DIR
218 CRYPTDIR="$ECRYPTFS_DIR/$USER/.$PRIVATE_DIR"
220 mkdir -p -m 700 $HOME/.ecryptfs
221 MOUNTPOINT="$HOME/$PRIVATE_DIR"
222 CRYPTDIR="$HOME/.$PRIVATE_DIR"
225 # Check for previously setup private directory
226 if [ -s "$HOME/.ecryptfs/wrapped-passphrase" -a "$FORCE" != "1" ]; then
227 error "$(gettext 'wrapped-passphrase file already exists, use --force to overwrite.')"
229 if [ -s "$HOME/.ecryptfs/$PRIVATE_DIR.sig" -a "$FORCE" != "1" ]; then
230 error "$PRIVATE_DIR.sig" "$(gettext 'file already exists, use --force to overwrite.')"
233 # Check for active mounts
234 grep -qs "$MOUNTPOINT " /proc/mounts && error "[$MOUNTPOINT]" "$(gettext 'is already mounted')"
235 grep -qs "$CRYPTDIR " /proc/mounts && error "[$CRYPTDIR]" "$(gettext 'is already mounted')"
237 # Check that the mount point and encrypted directory are empty (skip symlinks).
238 # Perhaps one day we could provide a migration mode (using rsync or something),
239 # but this would be VERY hard to do safely.
240 count=`ls -Al "$MOUNTPOINT" 2>/dev/null | egrep -c "^[drwx-]{10}"`
241 if [ "$count" != "0" ]; then
242 error "$MOUNTPOINT" "$(gettext 'must be empty before proceeding')"
244 count=`ls -Al "$CRYPTDIR" 2>/dev/null | egrep -c "^[dlrwx-]{10}"`
245 if [ "$count" != "0" ]; then
246 error "$CRYPTDIR" "$(gettext 'must be empty before proceeding')"
250 # Prompt for the LOGINPASS, if not on the command line and not in the env
251 if [ -z "$LOGINPASS" ] && ( [ "$BOOTSTRAP" != "1" ] || [ "$ECRYPTFS_MIGRATE" = "1" ] ); then
253 while [ $tries -lt $PW_ATTEMPTS ]; do
255 echo -n "$MESSAGE [$USER]: "
259 if [ $WRAPPING_PASS != "LOGIN" -o ! -x /sbin/unix_chkpwd ]; then
260 # If we can't check the accuracy of the user's entered
261 # passphrase, force them to type it twice (matching)
263 echo -n "$MESSAGE [$USER] (again): "
264 LOGINPASS2=`head -n1`
267 if [ "$LOGINPASS" != "$LOGINPASS2" ]; then
268 echo "$(gettext 'ERROR: ')" "$(gettext 'Wrapping passphrases must match')"
272 tries=$(($tries + 1))
275 if [ -z "$LOGINPASS" ]; then
276 echo "$(gettext 'ERROR: ')" "$(gettext 'You must provide a login passphrase')"
277 tries=$(($tries + 1))
279 if [ "$NOPWCHECK" = "1" ]; then
280 echo "$(gettext 'INFO:')" "$(gettext 'Skipping password verification')"
283 if printf "%s\0" "$LOGINPASS" | /sbin/unix_chkpwd "$USER" nullok; then
286 echo "$(gettext 'ERROR: ')" "$(gettext 'Your login passphrase is incorrect')"
287 tries=$(($tries + 1))
292 if [ $tries -ge $PW_ATTEMPTS ]; then
293 error "$(gettext 'Too many incorrect password attempts, exiting')"
297 # Prompt for the MOUNTPASS, if not on the command line and not in the env
298 if [ -z "$MOUNTPASS" ]; then
300 while [ $tries -lt $PW_ATTEMPTS ]; do
302 echo -n "$(gettext 'Enter your mount passphrase [leave blank to generate one]: ')"
306 if [ -z "$MOUNTPASS" ]; then
307 MOUNTPASS=`random_passphrase $KEYBYTES`
312 echo -n "$(gettext 'Enter your mount passphrase (again): ')"
313 MOUNTPASS2=`head -n1`
316 if [ "$MOUNTPASS" != "$MOUNTPASS2" ]; then
317 echo "$(gettext 'ERROR: ')" "$(gettext 'Mount passphrases do not match')"
318 tries=$(($tries + 1))
324 if [ $tries -ge $PW_ATTEMPTS ]; then
325 error "$(gettext 'Too many incorrect passphrase attempts, exiting')"
330 echo "************************************************************************"
331 echo "$(gettext 'YOU SHOULD RECORD YOUR MOUNT PASSPHRASE AND STORE IT IN A SAFE LOCATION.')"
332 echo " ecryptfs-unwrap-passphrase ~/.ecryptfs/wrapped-passphrase"
333 echo "$(gettext 'THIS WILL BE REQUIRED IF YOU NEED TO RECOVER YOUR DATA AT A LATER TIME.')"
334 echo "************************************************************************"
337 ###############################################################################
339 # Setup private directory in home
340 mkdir -m 700 -p "$CRYPTDIR" || error "$(gettext 'Could not create crypt directory')" "[$CRYPTDIR]"
341 mkdir -m 700 -p "$MOUNTPOINT" || error "$(gettext 'Could not create mount directory')" "[$MOUNTPOINT]"
342 ln -sf /usr/share/ecryptfs-utils/ecryptfs-mount-private.txt "$MOUNTPOINT"/README.txt
343 ln -sf /usr/share/ecryptfs-utils/ecryptfs-mount-private.desktop "$MOUNTPOINT"/Access-Your-Private-Data.desktop
344 chmod 500 "$MOUNTPOINT"
346 # Setup ~/.ecryptfs directory
347 if [ "$NOAUTOMOUNT" = "1" ]; then
348 echo "$(gettext 'INFO:')" "$HOME/$PRIVATE_DIR" "$(gettext 'will not be mounted on login')"
350 touch $HOME/.ecryptfs/auto-mount || error "$(gettext 'Could not setup ecryptfs auto-mount')"
352 if [ "$NOAUTOUMOUNT" = "1" ]; then
353 echo "$(gettext 'INFO:')" "$HOME/$PRIVATE_DIR" "$(gettext 'will not be unmounted on logout')"
355 touch $HOME/.ecryptfs/auto-umount || error "$(gettext 'Could not setup ecryptfs auto-umount')"
358 if [ "$WRAPPING_PASS" = "LOGIN" ]; then
359 rm -f $HOME/.ecryptfs/wrapping-independent || error "$(gettext 'Could not remove ecryptfs wrapping-independent')"
361 touch $HOME/.ecryptfs/wrapping-independent || error "$(gettext 'Could not setup ecryptfs wrapping-independent')"
365 # Backup any existing wrapped-passphrase or sig files; we DO NOT destroy this
366 timestamp=`date +%Y%m%d%H%M%S`
367 for i in "$HOME/.ecryptfs/wrapped-passphrase" "$HOME/.ecryptfs/$PRIVATE_DIR.sig"; do
369 mv -f "$i" "$i.$timestamp" || error "(gettext 'Could not backup existing data')" "[$i]"
373 # Setup wrapped-passphrase file
376 if [ -z "$LOGINPASS" ] && [ "$BOOTSTRAP" = "1" ]; then
377 # This will be wrapped by pam_ecryptfs's chauthtok as soon as the user
378 # chooses a password. Until that happens (hopefully soon), standard
379 # file permissions (600) are all that's protecting it. Write it to
380 # ramdisk, to keep it from leaking to the hard-drive.
381 temp=`mktemp /dev/shm/.ecryptfs-XXXXXX`
382 printf "%s" "$MOUNTPASS" > "$temp"
383 mv -f -T "$temp" "/dev/shm/.ecryptfs-$USER" || error "Could not create passphrase file"
385 printf "%s\n%s" "$MOUNTPASS" "$LOGINPASS" | ecryptfs-wrap-passphrase "$HOME/.ecryptfs/wrapped-passphrase" - || error "$(gettext 'Could not wrap passphrase')"
389 # Add the passphrase to current keyring
390 # On subsequent logins, this should be handled by "pam_ecryptfs.so unwrap"
391 response=`printf "%s" "$MOUNTPASS" | ecryptfs-add-passphrase $FNEK -`
392 if [ $? -ne 0 ]; then
393 error "$(gettext 'Could not add passphrase to the current keyring')"
395 sig=`echo "$response" | grep "Inserted auth tok" | sed "s/^.*\[//" | sed "s/\].*$//"`
396 if ! echo "$sig" | egrep -qs "^[0-9a-fA-F]{$KEYBYTES,$KEYBYTES}$"; then
397 error "$(gettext 'Could not obtain the key signature')"
400 echo "$sig" > "$temp" || error "$(gettext 'Could not create signature file')" "[$HOME/.ecryptfs/$PRIVATE_DIR.sig]"
401 mv "$temp" "$HOME/.ecryptfs/$PRIVATE_DIR.sig"
402 which restorecon 2>/dev/null && restorecon "$HOME/.ecryptfs/$PRIVATE_DIR.sig" > /dev/null 2>&1
404 echo "$MOUNTPOINT" > "$temp" || error "$(gettext 'Could not create mountpoint file')" "[$HOME/.ecryptfs/$PRIVATE_DIR.mnt]"
405 mv "$temp" "$HOME/.ecryptfs/$PRIVATE_DIR.mnt"
406 which restorecon 2>/dev/null && restorecon "$HOME/.ecryptfs/$PRIVATE_DIR.mnt" > /dev/null 2>&1
409 echo "$(gettext 'Done configuring.')"
412 # Skip the tests if we're in bootstrap mode, but exit with the encrypted
414 if [ "$BOOTSTRAP" = "1" ]; then
415 # Force the mount here, since the root user has the key loaded,
416 # and the calling 'adduser' is about to copy over /etc/skel
417 # NOTE: it is the responsibility of 'adduser' to unmount!
418 # And ensure that $USER owns the files/dirs we've created as root
419 chown $USER:$GROUP "$CRYPTDIR" /dev/shm/.ecryptfs-$USER
420 chown -R $USER:$GROUP $ECRYPTFS_DIR/$USER
421 chown -R $USER:$GROUP $MOUNTPOINT
422 if [ "$FNEK" = "--fnek" ]; then
423 fnek_sig=`tail -n 1 "$HOME/.ecryptfs/$PRIVATE_DIR.sig"`
424 sig=`head -n 1 "$HOME/.ecryptfs/$PRIVATE_DIR.sig"`
425 sig_opt="ecryptfs_sig=$sig,ecryptfs_fnek_sig=$fnek_sig"
427 sig_opt="ecryptfs_sig=$sig"
429 # Do the mount, and provide some helpful symlinks
430 mount -i -t ecryptfs -o "rw,$sig_opt,ecryptfs_cipher=$CIPHER,ecryptfs_key_bytes=$KEYBYTES" "$CRYPTDIR" "$MOUNTPOINT" || error "Could not mount"
431 ln -sf $ECRYPTFS_DIR/$USER/.ecryptfs $MOUNTPOINT/.ecryptfs
432 ln -sf $ECRYPTFS_DIR/$USER/.$PRIVATE_DIR $MOUNTPOINT/.$PRIVATE_DIR
433 chown -R $USER:$GROUP $ECRYPTFS_DIR/$USER
434 chown -R $USER:$GROUP $MOUNTPOINT
438 # Now let's perform some basic mount/write/umount/read sanity testing...
439 echo "$(gettext 'Testing mount/write/umount/read...')"
440 printf "%s" "$MOUNTPASS" | ecryptfs-add-passphrase $FNEK -
441 /sbin/mount.ecryptfs_private || error "$(gettext 'Could not mount private ecryptfs directory')"
442 temp=`mktemp "$MOUNTPOINT/ecryptfs.test.XXXXXX"` || error_testing "$temp" "$(gettext 'Could not create empty file')"
443 random_data=`head -c 16000 /dev/urandom | od -x` || error_testing "$temp" "$(gettext 'Could not generate random data')"
444 echo "$random_data" > "$temp" || error_testing "$temp" "$(gettext 'Could not write encrypted file')"
445 md5sum1=`md5sum "$temp"` || error_testing "$temp" "$(gettext 'Could not read encrypted file')"
446 /sbin/umount.ecryptfs_private || error_testing "$temp" "$(gettext 'Could not unmount private ecryptfs directory')"
447 printf "%s" "$MOUNTPASS" | ecryptfs-add-passphrase $FNEK -
448 /sbin/mount.ecryptfs_private || error_testing "$temp" "$(gettext 'Could not mount private ecryptfs directory (2)')"
449 md5sum2=`md5sum "$temp"` || error_testing "$temp" "$(gettext 'Could not read encrypted file (2)')"
451 # Use ecryptfs-umount-private on the final run, to clear the used keys
453 ecryptfs-umount-private || error_testing "$temp" "$(gettext 'Could not unmount private ecryptfs directory (2)')"
454 if [ "$md5sum1" != "$md5sum2" ]; then
455 error "$(gettext 'Testing failed.')"
457 echo "$(gettext 'Testing succeeded.')"
461 echo "$(gettext 'Logout, and log back in to begin using your encrypted directory.')"