summaryrefslogtreecommitdiff
path: root/mount.cifs.c
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2010-04-01 15:19:17 -0400
committerJeff Layton <jlayton@redhat.com>2010-04-01 15:19:17 -0400
commit294215ef969ce3ecb91063fbbb8a8c075272cc8d (patch)
tree3c28e1be371ef60f7e74ebab36e39484dd2b9572 /mount.cifs.c
parente87a203fbaf059831292f2cb9a0692ef7a78a267 (diff)
downloadcifs-utils-294215ef969ce3ecb91063fbbb8a8c075272cc8d.tar.gz
cifs-utils-294215ef969ce3ecb91063fbbb8a8c075272cc8d.tar.bz2
cifs-utils-294215ef969ce3ecb91063fbbb8a8c075272cc8d.zip
mount.cifs: introduce privilege separation
Much of the mount option parsing and other activities can be done by an unprivileged process. Allocate the parsed_mount_info struct as an anonymous mmap() segment and then fork to do the actual mount option parsing. The child can then drop root privileges before populating the parsed_mount_info struct. The parent waits for the child to exit and then continues the mount process based on the child's exit status. Signed-off-by: Jeff Layton <jlayton@redhat.com>
Diffstat (limited to 'mount.cifs.c')
-rw-r--r--mount.cifs.c96
1 files changed, 80 insertions, 16 deletions
diff --git a/mount.cifs.c b/mount.cifs.c
index 50c69a1..f4aea01 100644
--- a/mount.cifs.c
+++ b/mount.cifs.c
@@ -41,6 +41,8 @@
#include <fcntl.h>
#include <limits.h>
#include <fstab.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
#include "mount.h"
#include "util.h"
@@ -1115,6 +1117,34 @@ add_mtab_exit:
return rc;
}
+/* have the child drop root privileges */
+static int
+drop_child_privs(void)
+{
+ int rc;
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+
+ if (gid) {
+ rc = setgid(gid);
+ if (rc) {
+ fprintf(stderr, "Unable set group identity: %s\n",
+ strerror(errno));
+ return EX_SYSERR;
+ }
+ }
+ if (uid) {
+ rc = setuid(uid);
+ if (rc) {
+ fprintf(stderr, "Unable set user identity: %s\n",
+ strerror(errno));
+ return EX_SYSERR;
+ }
+ }
+
+ return 0;
+}
+
static int
assemble_mountinfo(struct parsed_mount_info *parsed_info,
const char *thisprogram, const char *mountpoint,
@@ -1122,7 +1152,10 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info,
{
int rc;
- /* sanity check for unprivileged mounts */
+ rc = drop_child_privs();
+ if (rc)
+ goto assemble_exit;
+
if (getuid()) {
rc = check_fstab(thisprogram, mountpoint, orig_dev,
&orgoptions);
@@ -1149,13 +1182,6 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info,
rc = EX_USAGE;
goto assemble_exit;
}
-
- if (geteuid()) {
- fprintf(stderr, "%s: not installed setuid - \"user\" "
- "CIFS mounts not supported.", thisprogram);
- rc = EX_FAIL;
- goto assemble_exit;
- }
}
parsed_info->flags &= ~(MS_USERS | MS_USER);
@@ -1232,10 +1258,17 @@ int main(int argc, char **argv)
size_t options_size = MAX_OPTIONS_LEN;
size_t dev_len;
struct parsed_mount_info *parsed_info = NULL;
+ pid_t pid;
if (check_setuid())
return EX_USAGE;
+ if (geteuid()) {
+ fprintf(stderr, "%s: not installed setuid root - \"user\" "
+ "CIFS mounts not supported.", thisprogram);
+ return EX_FAIL;
+ }
+
/* setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE); */
@@ -1249,9 +1282,13 @@ int main(int argc, char **argv)
if (thisprogram == NULL)
thisprogram = "mount.cifs";
- parsed_info = calloc(1, sizeof(*parsed_info));
- if (!parsed_info) {
- fprintf(stderr, "Unable to allocate memory.\n");
+ /* allocate parsed_info as shared anonymous memory range */
+ parsed_info = mmap(0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (parsed_info == (struct parsed_mount_info *) -1) {
+ parsed_info = NULL;
+ fprintf(stderr, "Unable to allocate memory: %s\n",
+ strerror(errno));
return EX_SYSERR;
}
@@ -1322,10 +1359,36 @@ int main(int argc, char **argv)
goto mount_exit;
}
- rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
- orig_dev, orgoptions);
- if (rc)
+ /*
+ * mount.cifs does privilege separation. Most of the code to handle
+ * assembling the mount info is done in a child process that drops
+ * privileges. The info is assembled in parsed_info which is a
+ * shared, mmaped memory segment. The parent waits for the child to
+ * exit and checks the return code. If it's anything but "0", then
+ * the process exits without attempting anything further.
+ */
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
+ rc = EX_SYSERR;
goto mount_exit;
+ } else if (!pid) {
+ /* child */
+ rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
+ orig_dev, orgoptions);
+ return rc;
+ } else {
+ /* parent */
+ pid = wait(&rc);
+ if (!WIFEXITED(rc)) {
+ fprintf(stderr, "Child process terminated abnormally.\n");
+ rc = EX_SYSERR;
+ goto mount_exit;
+ }
+ rc = WEXITSTATUS(rc);
+ if (rc)
+ goto mount_exit;
+ }
options = calloc(options_size, 1);
if (!options) {
@@ -1440,9 +1503,10 @@ mount_retry:
rc = add_mtab(dev_name, mountpoint, parsed_info->flags);
mount_exit:
- if (parsed_info)
+ if (parsed_info) {
memset(parsed_info->password, 0, sizeof(parsed_info->password));
- SAFE_FREE(parsed_info);
+ munmap(parsed_info, sizeof(*parsed_info));
+ }
SAFE_FREE(dev_name);
SAFE_FREE(options);
SAFE_FREE(orgoptions);