File CVE-2011-1831,1832,1834.patch of Package ecryptfs-utils.import4986

Description: fix privilege escalation via mountpoint race conditions
Author: Dan Rosenberg
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ecryptfs-utils/+bug/732628

Index: ecryptfs-utils-83/src/utils/mount.ecryptfs_private.c
===================================================================
--- ecryptfs-utils-83.orig/src/utils/mount.ecryptfs_private.c
+++ ecryptfs-utils-83/src/utils/mount.ecryptfs_private.c
@@ -152,6 +152,47 @@ char *fetch_sig(char *pw_dir, int entry)
 	return sig;
 }
 
+int check_ownership_mnt(int uid, char **mnt) {
+/* Check ownership of mount point, chdir into it, and
+ * canonicalize the path for use in mtab updating.
+ * Return 0 if everything is in order, 1 on error.
+ */
+	struct stat s;
+	char *cwd;
+
+	/* From here on, we'll refer to "." as our mountpoint, to avoid
+	 * races.
+	 */
+	if (chdir(*mnt) != 0) {
+		fputs("Cannot chdir into mountpoint.\n", stderr);
+		return 1;
+	}
+	if (stat(".", &s) != 0) {
+		fputs("Cannot examine mountpoint.\n", stderr);
+		return 1;
+	}
+	if (!S_ISDIR(s.st_mode)) {
+		fputs("Mountpoint is not a directory.\n", stderr);
+		return 1;
+	}
+	if (s.st_uid != uid) {
+		fputs("You do not own that mountpoint.\n", stderr);
+		return 1;
+	}
+
+	/* Canonicalize our pathname based on the current directory to
+	 * avoid races.
+	 */
+	cwd = getcwd(NULL, 0);
+	if (!cwd) {
+		fputs("Failed to get current directory\n", stderr);
+		return 1;
+	}
+	*mnt = cwd;
+	return 0;
+}
+
+
 int check_ownerships(int uid, char *path) {
 /* Check ownership of device and mount point.
  * Return 0 if everything is in order, 1 on error.
@@ -177,31 +218,77 @@ int update_mtab(char *dev, char *mnt, ch
 /* Update /etc/mtab with new mount entry.
  * Return 0 on success, 1 on failure.
  */
-	FILE *fh;
-	struct mntent m;
-	fh = setmntent("/etc/mtab", "a");
-	if (fh == NULL) {
+	int fd;
+	FILE *old_mtab, *new_mtab;
+	struct mntent *old_ent, new_ent;
+
+	/* Make an attempt to play nice with other mount helpers
+	 * by creating an /etc/mtab~ lock file. Of course this
+	 * only works if those other helpers actually check for
+	 * this.
+	 */
+	fd = open("/etc/mtab~", O_RDONLY | O_CREAT | O_EXCL, 0644);
+	if (fd < 0) {
+		perror("open");
+		return 1;
+	}
+	close(fd);
+
+	old_mtab = setmntent("/etc/mtab", "r");
+	if (old_mtab == NULL) {
 		perror("setmntent");
-		/* Unmount if mtab cannot be updated */
-		umount(mnt);
 		return 1;
 	}
-	m.mnt_fsname = dev;
-	m.mnt_dir = mnt;
-	m.mnt_type = FSTYPE;
-	m.mnt_opts = opt;
-	m.mnt_freq = 0;
-	m.mnt_passno = 0;
-	flockfile(fh);
-	if (addmntent(fh, &m) != 0) {
+
+	new_mtab = setmntent("/etc/mtab.tmp", "w");
+	if (new_mtab == NULL) {
+		perror("setmntent");
+		goto fail_early;
+	}
+
+	while (old_ent = getmntent(old_mtab)) {
+		if (addmntent(new_mtab, old_ent) != 0) {
+			perror("addmntent");
+			goto fail;
+		}
+	}
+	endmntent(old_mtab);
+
+	new_ent.mnt_fsname = dev;
+	new_ent.mnt_dir = mnt;
+	new_ent.mnt_type = FSTYPE;
+	new_ent.mnt_opts = opt;
+	new_ent.mnt_freq = 0;
+	new_ent.mnt_passno = 0;
+
+	if (addmntent(new_mtab, &new_ent) != 0) {
 		perror("addmntent");
-		endmntent(fh);
-		/* Unmount if mtab cannot be updated */
-		umount(mnt);
-		return 1;
+		goto fail;
+	}
+
+	if (fchmod(fileno(new_mtab), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
+		perror("fchmod");
+		goto fail;
+	}
+	endmntent(new_mtab);
+
+	if (rename("/etc/mtab.tmp", "/etc/mtab") < 0) {
+		perror("rename");
+		goto fail_late;
 	}
-	endmntent(fh);
+
+	unlink("/etc/mtab~");
+
 	return 0;
+
+fail:
+	endmntent(new_mtab);
+fail_late:
+	unlink("/etc/mtab.tmp");
+fail_early:
+	endmntent(old_mtab);
+	unlink("/etc/mtab~");
+	return 1;
 }
 
 FILE *lock_counter(char *u, int uid) {
@@ -431,8 +518,9 @@ int main(int argc, char *argv[]) {
 		}
 	}
 
-	/* Check ownership of mnt */
-	if (check_ownerships(uid, mnt) != 0) {
+	/* Check ownership of the mountpoint. From here on, dest refers
+	 * to a canonicalized path, and the mountpoint is the cwd. */
+	if (check_ownership_mnt(uid, &mnt) != 0) {
 		goto fail;
 	}
 
@@ -462,7 +550,7 @@ int main(int argc, char *argv[]) {
 		 */
 		setreuid(-1, 0);
 		/* Perform mount */
-		if (mount(dev, mnt, FSTYPE, 0, opt) == 0) {
+		if (mount(dev, ".", FSTYPE, 0, opt) == 0) {
 			if (update_mtab(dev, mnt, opt) != 0) {
 				goto fail;
 			}
@@ -492,7 +580,7 @@ int main(int argc, char *argv[]) {
 		 * Do not use the umount.ecryptfs helper (-i).
  		 */
 		setresuid(0,0,0);
-		execl("/bin/umount", "umount", "-i", "-l", mnt, NULL);
+		execl("/bin/umount", "umount", "-i", "-l", ".", NULL);
 		perror("execl unmount failed");
 		goto fail;
 	}
openSUSE Build Service is sponsored by