[PATCH] mount.ecryptfs_private.c: add per-user locking I have switched my email address to @canonical.com in the header.
This patch adds a simple locking mechanism, that allows a user to only run one of mount.ecryptfs_private or umount.ecryptfs_private at a time. This lock is intended to avoid race conditions where a directory would be mounted/unmounted simultaneously during the course of the running program. Note that the current locking mechanism only protects a user from multiple instances of himself; root could still produce a race by perhaps mounting/unmounting the ecryptfs_private directory while the user is running this script. Also it does not solve the race where device/mount permissions might change during the course of the run. That check (check_ownerships()) is merely provided as a convenience to the user--there are no privilege escalation implications of that race. This patch creates two functions, acquire_lock() and release_lock(), which will create/delete a lock file: * /var/lock/ecryptfs_private.$USERNAME Should lock acquisition fail, the program will return 1 rather than sleeping and retrying. Where such behavior is desired, that should be handled by a wrapper program or calling script. The setuid-nature of this program demands that it run-to-completion. This patch re-factored the main() function such that main() obtains and releases the lock, calling locked_main() in the process. The overwhelming majority of the functionality that used to be in main() is now in locked_main(). A fork()/wait() is now required surrounding the execl(unmount) call in order to release the lock after that exec completes. The patch also re-factors the code in check_ownerships() such that it can be called repeatedly on different directory paths. Inline documentation was updated accordingly. Signed-off-by: Dustin Kirkland <[EMAIL PROTECTED]> -- :-Dustin Dustin Kirkland Ubuntu Server Developer Canonical, LTD [EMAIL PROTECTED] GPG: 1024D/83A61194
diff --git a/src/utils/mount.ecryptfs_private.c b/src/utils/mount.ecryptfs_private.c
index 108fd3f..91b00a3 100644
--- a/src/utils/mount.ecryptfs_private.c
+++ b/src/utils/mount.ecryptfs_private.c
@@ -4,7 +4,7 @@
*
* Copyright (C) 2008 Canonical Ltd.
*
- * This code was originally written by Dustin Kirkland <[EMAIL PROTECTED]>.
+ * This code was originally written by Dustin Kirkland <[EMAIL PROTECTED]>.
*
* 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
@@ -28,6 +28,7 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <fcntl.h>
#include <keyutils.h>
#include <mntent.h>
#include <pwd.h>
@@ -45,6 +46,7 @@
#define KEY_CIPHER "aes"
#define PRIVATE_DIR "Private"
#define FSTYPE "ecryptfs"
+#define LOCK_FILE "/var/lock/ecryptfs_private"
int check_username(char *u) {
@@ -133,13 +135,13 @@ char *fetch_sig(char *pw_dir) {
}
-int check_ownerships(int uid, char *dev, char *mnt) {
-/* Check ownership of device and mount point.
+int check_ownerships(int uid, char *dir) {
+/* Check ownership of device or mount point.
* Return 0 if everything is in order, 1 on error.
*/
struct stat s;
- if (stat(dev, &s) != 0) {
- fputs("Cannot examine encrypted directory\n", stderr);
+ if (stat(dir, &s) != 0) {
+ fputs("Cannot examine directory\n", stderr);
return 1;
}
if (!S_ISDIR(s.st_mode)) {
@@ -147,15 +149,7 @@ int check_ownerships(int uid, char *dev, char *mnt) {
return 1;
}
if (s.st_uid != uid) {
- fputs("You do not own that encrypted directory\n", stderr);
- return 1;
- }
- if (stat(mnt, &s) != 0) {
- fputs("Cannot examine mount directory\n", stderr);
- return 1;
- }
- if (s.st_uid != uid) {
- fputs("You do not own that mount directory\n", stderr);
+ fputs("You do not own that directory\n", stderr);
return 1;
}
return 0;
@@ -253,20 +247,39 @@ int update_mtab(char *dev, char *mnt, char *opt) {
return 0;
}
+int acquire_lock(char *lockfile) {
+/* Simple lock acquisition function.
+ * Return 1 if the lock cannot be acquire. Note that we do not sleep or
+ * wait to attain the lock. Due to the setuid-nature of this program,
+ * we want to run-to-completion. Wrappers or callers of this program should
+ * handle any necessary retries.
+ * Return 0 on success.
+ */
+ int fh;
+ fh = open(lockfile, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ if (fh < 0) {
+ perror(lockfile);
+ return 1;
+ }
+ close(fh);
+ return 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.
- *
- * A single executable is created and hardlinked to two different names.
- * The mode of operation (mounting|unmounting) is determined by examining
- * the name of the executable. "Mounting" mode is assumed, unless the
- * executable contains the string "umount".
- * Example:
- * /sbin/mount.ecryptfs_private
- * /sbin/umount.ecryptfs_private
+void release_lock(char *lockfile) {
+/* Simple lock release function.
+ * If we cannot delete the lock, print an error message. However, there's
+ * not much more we can do, as we should be euid=0 here.
+ */
+ if (unlink(lockfile) != 0) {
+ perror("unlink");
+ }
+}
+
+
+/* This function performs the bulk of the work in this program, and only runs
+ * if an appropriate lock is established.
*
- * At the moment, this program:
+ * At the moment, this function:
* - mounts ~/.Private onto ~/Private
* - as an ecryptfs filesystem
* - using the AES cipher
@@ -282,53 +295,29 @@ int update_mtab(char *dev, char *mnt, char *opt) {
* - has the signature's key in his keyring
* - owns both ~/.Private and ~/Private
* - is currently mounted
- *
- * The only setuid operations in this program are:
- * a) mounting
- * b) unmounting
- * c) updating /etc/mtab
+ * - returns 0 on success
+ * - returns 1 on any failure
*/
-int main(int argc, char *argv[]) {
- int uid, rc, mounting;
- struct passwd *pwd;
+int locked_main(int mounting, struct passwd *pwd) {
char *dev, *mnt, *opt;
char *sig;
-
- uid = getuid();
- if ((pwd = getpwuid(uid)) == NULL) {
- perror("getpwuid");
- return 1;
- }
+ int pid, rc;
/* Non-privileged effective uid is sufficient for all but the code
* that mounts, unmounts, and updates /etc/mtab.
* Run at a lower privilege until we need it.
*/
- if (seteuid(uid)<0 || geteuid()!=uid) {
- perror("setuid");
+ if (seteuid(pwd->pw_uid)<0 || geteuid()!=pwd->pw_uid) {
+ perror("seteuid");
return 1;
}
- if (check_username(pwd->pw_name) != 0) {
- /* Must protect against a crafted user=john,suid from entering
- * filesystem options
- */
- return 1;
- }
-
- /* Determine if mounting or unmounting by looking at the invocation */
- if (strstr(argv[0], "umount") == NULL) {
- mounting = 1;
- } else {
- mounting = 0;
- }
-
/* Fetch signature from file */
if ((sig = fetch_sig(pwd->pw_dir)) == NULL) {
return 1;
}
- /* Construct device, mount point, and mount options */
+ /* Construct device, mount point, mount options */
if (
(asprintf(&dev, "%s/.%s", pwd->pw_dir, PRIVATE_DIR) < 0) ||
dev == NULL) {
@@ -350,7 +339,8 @@ int main(int argc, char *argv[]) {
}
/* Check ownership of dev and mnt */
- if (check_ownerships(uid, dev, mnt) != 0) {
+ if (check_ownerships(pwd->pw_uid, dev) != 0 ||
+ check_ownerships(pwd->pw_uid, mnt) != 0 ) {
return 1;
}
@@ -375,6 +365,8 @@ int main(int argc, char *argv[]) {
if (update_mtab(dev, mnt, opt) != 0) {
return 1;
}
+ /* Successful mount */
+ return 0;
} else {
perror("mount");
return 1;
@@ -391,9 +383,94 @@ int main(int argc, char *argv[]) {
* Do not use the umount.ecryptfs helper (-i).
*/
setresuid(0,0,0);
- execl("/bin/umount", "umount", "-i", mnt, NULL);
- perror("execl unmount failed");
+ if ((pid = fork()) < 0) {
+ perror("fork");
+ return 1;
+ }
+ if (pid == 0) {
+ execl("/bin/umount", "umount", "-i", mnt, NULL);
+ perror("execl unmount failed");
+ return 1;
+ } else {
+ wait(&rc);
+ return rc;
+ }
+ }
+ /* It should be impossible to reach here */
+ return 1;
+}
+
+
+/* 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.
+ *
+ * A single executable is created and hardlinked to two different names.
+ * The mode of operation (mounting|unmounting) is determined by examining
+ * the name of the executable. "Mounting" mode is assumed, unless the
+ * executable contains the string "umount".
+ * Example:
+ * /sbin/mount.ecryptfs_private
+ * /sbin/umount.ecryptfs_private
+ *
+ * A lock in /var/lock/ecryptfs_private.USERNAME is used to ensure that a given
+ * user does not run this program more than once at a time, due to some race
+ * that can arise in such situations.
+ *
+ * The only setuid operations in this program are:
+ * a) acquiring the lock
+ * b) mounting
+ * c) unmounting
+ * d) updating /etc/mtab
+ * e) releasing the lock
+ *
+ * The sanity checking of the environment, mounting, and umounting is
+ * performed by the locked_main() function, once the lock is established.
+ * This functional separation helps ensure that the lock is cleaned up in
+ * ALL failure situations by handling it in a single place in the main()
+ * function.
+ */
+int main(int argc, char *argv[]) {
+ int mounting, uid, rc;
+ struct passwd *pwd;
+ char *lockfile;
+
+ /* Determine if mounting or unmounting by looking at the invocation */
+ if (strstr(argv[0], "umount") == NULL) {
+ mounting = 1;
+ } else {
+ mounting = 0;
+ }
+ uid = getuid();
+ if ((pwd = getpwuid(uid)) == NULL) {
+ perror("getpwuid");
return 1;
}
- return 0;
+ if (check_username(pwd->pw_name) != 0) {
+ /* Must protect against a crafted user=john,suid from entering
+ * filesystem options
+ */
+ return 1;
+ }
+
+ /* Acquire lock file, assuring that only one instance of this program
+ * per user runs at once.
+ */
+ if (
+ (asprintf(&lockfile, "%s.%s", LOCK_FILE, pwd->pw_name) < 0) ||
+ lockfile == NULL) {
+ perror("asprintf (lockfile)");
+ return 1;
+ }
+ if (acquire_lock(lockfile) != 0) {
+ return 1;
+ }
+ /*******************************************************************/
+ rc = locked_main(mounting, pwd);
+ /*******************************************************************/
+ if (seteuid(0)<0 || geteuid()!=0) {
+ perror("seteuid");
+ }
+ release_lock(lockfile);
+ return rc;
}
signature.asc
Description: This is a digitally signed message part
------------------------------------------------------------------------- Check out the new SourceForge.net Marketplace. It's the best place to buy or sell services for just about anything Open Source. http://sourceforge.net/services/buy/index.php
_______________________________________________ eCryptfs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/ecryptfs-devel
