[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 eCryptfs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ecryptfs-devel