From patchwork Fri Mar 26 14:25:39 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 48648 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.samba.org (fn.samba.org [216.83.154.106]) by ozlabs.org (Postfix) with ESMTP id 46E3CB7C09 for ; Sat, 27 Mar 2010 01:26:27 +1100 (EST) Received: from fn.samba.org (localhost [127.0.0.1]) by lists.samba.org (Postfix) with ESMTP id 33D6C46644; Fri, 26 Mar 2010 08:26:27 -0600 (MDT) X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on fn.samba.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.8 tests=BAYES_00, RCVD_IN_DNSWL_HI, SPF_HELO_PASS,SPF_NEUTRAL autolearn=ham version=3.2.5 X-Original-To: linux-cifs-client@lists.samba.org Delivered-To: linux-cifs-client@lists.samba.org Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by lists.samba.org (Postfix) with ESMTP id ED07B4665B for ; Fri, 26 Mar 2010 08:25:51 -0600 (MDT) Received: from int-mx05.intmail.prod.int.phx2.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.18]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o2QEPogZ005360 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 26 Mar 2010 10:25:51 -0400 Received: from localhost.localdomain (vpn-10-105.rdu.redhat.com [10.11.10.105]) by int-mx05.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o2QEPdCl026868 for ; Fri, 26 Mar 2010 10:25:50 -0400 From: Jeff Layton To: linux-cifs-client@lists.samba.org Date: Fri, 26 Mar 2010 10:25:39 -0400 Message-Id: <1269613542-6402-17-git-send-email-jlayton@samba.org> In-Reply-To: <1269613542-6402-1-git-send-email-jlayton@samba.org> References: <1269613542-6402-1-git-send-email-jlayton@samba.org> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.18 Subject: [linux-cifs-client] [PATCH 16/19] mount.cifs: introduce privilege separation X-BeenThere: linux-cifs-client@lists.samba.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: The Linux CIFS VFS client List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-cifs-client-bounces@lists.samba.org Errors-To: linux-cifs-client-bounces@lists.samba.org From: Jeff Layton 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 --- mount.cifs.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 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 #include #include +#include +#include #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);