← Back to team overview

ecryptfs-devel team mailing list archive

[PATCH] mount.ecryptfs_private/umount.ecryptfs_private counter

 

[PATCH] mount.ecryptfs_private/umount.ecryptfs_private counter

We have been receiving bug reports about "random" unmounts of
encrypted private directories.  Rather than truly "random", these were
undesired, and caused usually by cronjobs or additional ssh/console
login sessions.  Each of these would trigger a pam close session,
which would call umount.ecryptfs_private.

To solve this correctly, we need a counter, incremented each time a
mount.ecryptfs_private is successfully attempted, and a decrement each
time umount.ecryptfs_private is successfully attempted, as well as
zero'ing out the counter when an unmount succeeds.  A umount is then
only actually attempted when the counter reaches 0 (or if the user
overrides the counter with a -f option).  Alternatively, the user can
remove their counter file at any time (interpreted as a count of 0).

This patch also updates the manpage to describe the -f option.

We are about to carry this patch in Ubuntu Intrepid, but any feedback
would be much appreciated.

Signed-off-by: Dustin Kirkland <kirkland@xxxxxxxxxxxxx>
--- ecryptfs-utils-53/doc/manpage/umount.ecryptfs_private.1	2008-07-23 11:17:30.000000000 -0500
+++ ecryptfs-utils-53/doc/manpage/umount.ecryptfs_private.1	2008-10-16 23:08:23.057270798 -0500
@@ -3,7 +3,13 @@
 umount.ecryptfs_private \- eCryptfs private unmount helper.
 
 .SH SYNOPSIS
-\fBumount.ecryptfs_private\fP
+\fBumount.ecryptfs_private\fP [\-f]
+
+.SH OPTIONS
+Options available for the \fBumount.ecryptfs_private\fP command:
+.TP
+.B \-f
+Force the unmount, ignoring the value of the mount counter in \fI/tmp/ecryptfs-USERNAME-Private\fP
 
 .SH DESCRIPTION
 \fBumount.ecryptfs_private\fP is a mount helper utility for non-root users to unmount a cryptographically mounted private directory, ~/Private.
@@ -12,6 +18,7 @@ If, and only if:
   - the private mount passphrase is in their kernel keyring, and
   - the current user owns both ~/.Private and ~/Private, and
   - ~/.Private is currently mounted on ~/Private
+  - the mount counter is 0 (counter is ignored if \-f option is used)
 
 This program will:
   - unmount ~/Private
@@ -27,6 +34,8 @@ The system administrator can add the pam
 
 \fI~/.ecryptfs/Private.sig\fP - file containing signature of mountpoint passphrase
 
+\fI/tmp/ecryptfs-USERNAME-Private\fP - file containing the mount counter, incremented on each mount, decremented on each unmount
+
 .SH SEE ALSO
 .PD 0
 .TP
--- ecryptfs-utils-53/src/utils/mount.ecryptfs_private.c	2008-10-16 22:52:50.545866066 -0500
+++ ecryptfs-utils-53/src/utils/mount.ecryptfs_private.c	2008-10-16 23:24:33.756948619 -0500
@@ -25,6 +25,7 @@
  */
 
 
+#include <sys/file.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -254,6 +255,87 @@ int update_mtab(char *dev, char *mnt, ch
 }
 
 
+int bump_counter(char *u, int delta) {
+/* Maintain a mount counter
+ *   increment on delta = 1
+ *   decrement on delta = -1
+ *   remove the counter file on delta = 0
+ *   return the updated count, negative on error
+ */
+	char *f;
+	FILE *fh;
+	struct stat s;
+	mode_t m;
+	int count;
+	/* We expect /tmp to exist, be writeable by the user,
+	 * and for this file to be cleared on boot */
+	if (
+		asprintf(&f, "/tmp/%s-%s-%s", FSTYPE, u, PRIVATE_DIR) < 0
+	   ) {
+		perror("asprintf");
+		return -1;
+	}
+	if (delta == 0) {
+		/* Try to remove the counter file */
+		return unlink(f);
+	}
+	if (stat(f, &s) == 0) {
+		/* If the counter file exists, open for reading/writing */
+		fh = fopen(f, "r+");
+	} else {
+		/* If the counter file does not exist, create for writing */
+		m = umask(0177);
+		fh = fopen(f, "w+");
+		umask(m);
+	}
+	if (fh == NULL) {
+		perror("fopen");
+		return -1;
+	}
+	/* Lock the file for reading/writing */
+	if (flock(fileno(fh), LOCK_EX) != 0) {
+		perror("flock");
+		return -1;
+	}
+	rewind(fh);
+	/* Read the count from file, default to 0 */
+	if (fscanf(fh, "%d\n", &count) != 1) {
+		count = 0;
+	}
+	/* Increment/decrement the counter */
+	count += delta;
+	if (count < 0) {
+		/* Don't set a count less than 0 */
+		count = 0;
+	}
+	/* Write the count to file */
+	rewind(fh);
+	fprintf(fh, "%d\n", count);
+	/* Unlock the file */
+	if (flock(fileno(fh), LOCK_UN) != 0) {
+		perror("flock");
+	}
+	return count;
+}
+
+
+int increment(char *u) {
+/* Bump counter up */
+	return bump_counter(u, 1);
+}
+
+
+int decrement(char *u) {
+/* Bump counter down */
+	return bump_counter(u, -1);
+}
+
+int zero(char *u) {
+/* Remove the counter file */
+	return bump_counter(u, 0);
+}
+
+
 /* This program is a setuid-executable allowing a non-privileged user to mount
  * and unmount an ecryptfs private directory.  This program is necessary to
  * keep from adding such entries to /etc/fstab.
@@ -289,7 +371,7 @@ int update_mtab(char *dev, char *mnt, ch
  *  c) updating /etc/mtab
  */
 int main(int argc, char *argv[]) {
-	int uid, rc, mounting;
+	int uid, rc, mounting, force;
 	struct passwd *pwd;
 	char *dev, *mnt, *opt;
 	char *sig;
@@ -321,6 +403,12 @@ int main(int argc, char *argv[]) {
 		mounting = 1;
 	} else {
 		mounting = 0;
+		/* Determine if unmounting is forced */
+		if (argv[1] != NULL && strncmp(argv[1], "-f", 2) == 0) {
+			force = 1;
+		} else {
+			force = 0;
+		}
 	}
 
 	/* Fetch signature from file */
@@ -355,6 +443,8 @@ int main(int argc, char *argv[]) {
 	}
 
 	if (mounting == 1) {
+		/* Increment mount counter, errors non-fatal */
+		increment(pwd->pw_name);
 		/* Mounting, so exit if already mounted */
 		if (is_mounted(dev, mnt, sig, mounting) == 1) {
 			/* fputs("Already mounted\n", stderr); */
@@ -380,6 +470,15 @@ int main(int argc, char *argv[]) {
 			return 1;
 		}
 	} else {
+		/* Decrement counter, exiting if >0, and non-forced unmount */
+		if (decrement(pwd->pw_name) > 0 && force != 1) {
+			fputs("Sessions still open, not unmounting\n", stderr);
+			return 1;
+		}
+		/* If we have made it here, zero the counter,
+ 		 * as we are going to unmount.
+ 		 */
+		zero(pwd->pw_name);
 		/* Unmounting, so exit if not mounted */
 		if (is_mounted(dev, mnt, sig, mounting) == 0) {
 			/* fputs("Not currently mounted\n", stderr); */