From patchwork Mon Sep 19 20:46:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Seth Forshee X-Patchwork-Id: 671941 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) by ozlabs.org (Postfix) with ESMTP id 3sdHxB5xcXz9s9x; Tue, 20 Sep 2016 06:46:38 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical-com.20150623.gappssmtp.com header.i=@canonical-com.20150623.gappssmtp.com header.b=Nybc6qMI; dkim-atps=neutral Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.76) (envelope-from ) id 1bm5SU-00032e-0v; Mon, 19 Sep 2016 20:46:34 +0000 Received: from mail-oi0-f44.google.com ([209.85.218.44]) by huckleberry.canonical.com with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.76) (envelope-from ) id 1bm5SP-00031p-TK for kernel-team@lists.ubuntu.com; Mon, 19 Sep 2016 20:46:30 +0000 Received: by mail-oi0-f44.google.com with SMTP id t83so84110933oie.3 for ; Mon, 19 Sep 2016 13:46:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical-com.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id; bh=Me7RDweb2+kJ/gDhVd9gE+viOBNV6vxqtA6sU936B3Y=; b=Nybc6qMISAhMIsoioVS7PG+MpAXaTJULiY2yTgaWJIZ5HoZl2R8OwE5I9zbMyFfMpr 3si1F2IId4TCK2QqqQkLqMPVwkvHMJboWLuufQfOpQuu13qWQraXZ7U6XWwzu0GBLvtz 2ChF9gVOVm/w17geDrMrSUONAzpaeR7ztNcSZwtCzJwQgcGwf7rC3Ig//sL1fh9m1Cst gFUNnBXZ4nyBy9L9BEmfBCOD0skzXYcB5HDBuoDH2I9nDkKrhHYNJiWy/WNGknHYE8ag 6Kl01fQ1u29dy1CRcBHAoMhALkODk78XCMqRmEKJ3ADyzh7ffZtihH9PB2WZYWZW1gV+ KKSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id; bh=Me7RDweb2+kJ/gDhVd9gE+viOBNV6vxqtA6sU936B3Y=; b=FeQVjFVw2y/QN4pVlEpLrXFtJZaMD4gRGLbsHWxvCYwrCQCtdyZCb1hDegaIxueOJa rSThwauwUXQehgnJAIW9idoi0Z7GugHP11p3kYHqHCZx2GkB8cPwCvZptZUf2E21Swu3 00NFkD7Wqp3BopKR6mTaWVzUqcQT7bgT+5FeFbnhiNmNE21VK45YhZStnUAkhoaivOrz uADrZWoYjUDlp3F5/unf6h12E30c16xZthiSVSoGUOy++2OVsR1QCmwMjef4trb/ZpPF ZM/CztwNYyd6axhyUa58uX36eJyvBN1Fr5RM7xLKvWrB4cZUSqCaxmgCh1DSi6eyDyh3 b5TA== X-Gm-Message-State: AE9vXwMLBeWNioZx4wwbU8mgP1TjrdNf8XwpEpwhFLlR3gPm9e5uMLQPT5fyiVCnTwpUcEWM X-Received: by 10.202.170.21 with SMTP id t21mr32163984oie.192.1474317988460; Mon, 19 Sep 2016 13:46:28 -0700 (PDT) Received: from localhost ([2605:a601:aa9:6620:f55b:b63:59a1:36ab]) by smtp.gmail.com with ESMTPSA id l37sm7705256otd.20.2016.09.19.13.46.27 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 19 Sep 2016 13:46:27 -0700 (PDT) From: Seth Forshee To: kernel-team@lists.ubuntu.com Subject: [PATCH][yakkety] UBUNTU: SAUCE: (namespace) block_dev: Forbid unprivileged mounting when device is opened for writing Date: Mon, 19 Sep 2016 15:46:26 -0500 Message-Id: <1474317986-47617-1-git-send-email-seth.forshee@canonical.com> X-Mailer: git-send-email 2.7.4 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.14 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: kernel-team-bounces@lists.ubuntu.com For unprivileged mounts to be safe the user must not be able to make changes to the backing store while it is mounted. This patch takes a step towards preventing this by refusing to mount in a user namepspace if the block device is open for writing and refusing attempts to open the block device for writing by non- root while it is mounted in a user namespace. To prevent this from happening we use i_writecount in the inodes of the bdev filesystem similarly to how it is used for regular files. Whenever the device is opened for writing i_writecount is checked; if it is negative the open returns -EBUSY, otherwise i_writecount is incremented. On mount, a positive i_writecount results in mount_bdev returning -EBUSY, otherwise i_writecount is decremented. Opens by root and mounts from init_user_ns do not check nor modify i_writecount. Signed-off-by: Seth Forshee --- This patch was mistakenly dropped in the rebase from xenial to yakkety. fs/block_dev.c | 17 +++++++++++++++++ fs/super.c | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index 0bfe3e2..1fd10d4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1582,6 +1582,20 @@ static int blkdev_open(struct inode * inode, struct file * filp) if (bdev == NULL) return -ENOMEM; + /* + * A negative i_writecount for bdev->bd_inode means that the bdev + * or one of its paritions is mounted in a user namespace. Deny + * writing for non-root in this case, otherwise an unprivileged + * user can attack the kernel by modifying the backing store of a + * mounted filesystem. + */ + if ((filp->f_mode & FMODE_WRITE) && + !file_ns_capable(filp, &init_user_ns, CAP_SYS_ADMIN) && + !atomic_inc_unless_negative(&bdev->bd_inode->i_writecount)) { + bdput(bdev); + return -EBUSY; + } + filp->f_mapping = bdev->bd_inode->i_mapping; return blkdev_get(bdev, filp->f_mode, filp); @@ -1683,6 +1697,9 @@ EXPORT_SYMBOL(blkdev_put); static int blkdev_close(struct inode * inode, struct file * filp) { struct block_device *bdev = I_BDEV(bdev_file_inode(filp)); + if (filp->f_mode & FMODE_WRITE && + !file_ns_capable(filp, &init_user_ns, CAP_SYS_ADMIN)) + atomic_dec(&bdev->bd_inode->i_writecount); blkdev_put(bdev, filp->f_mode); return 0; } diff --git a/fs/super.c b/fs/super.c index c2ff475..6022546 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1033,6 +1033,23 @@ struct dentry *mount_bdev(struct file_system_type *fs_type, if (IS_ERR(bdev)) return ERR_CAST(bdev); + if (current_user_ns() != &init_user_ns) { + /* + * For userns mounts, disallow mounting if bdev is open for + * writing + */ + if (!atomic_dec_unless_positive(&bdev->bd_inode->i_writecount)) { + error = -EBUSY; + goto error_bdev; + } + if (bdev->bd_contains != bdev && + !atomic_dec_unless_positive(&bdev->bd_contains->bd_inode->i_writecount)) { + atomic_inc(&bdev->bd_inode->i_writecount); + error = -EBUSY; + goto error_bdev; + } + } + /* * once the super is inserted into the list by sget, s_umount * will protect the lockfs code from trying to start a snapshot @@ -1042,7 +1059,7 @@ struct dentry *mount_bdev(struct file_system_type *fs_type, if (bdev->bd_fsfreeze_count > 0) { mutex_unlock(&bdev->bd_fsfreeze_mutex); error = -EBUSY; - goto error_bdev; + goto error_inc; } s = sget(fs_type, test_bdev_super, set_bdev_super, flags | MS_NOSEC, bdev); @@ -1054,7 +1071,7 @@ struct dentry *mount_bdev(struct file_system_type *fs_type, if ((flags ^ s->s_flags) & MS_RDONLY) { deactivate_locked_super(s); error = -EBUSY; - goto error_bdev; + goto error_inc; } /* @@ -1085,6 +1102,12 @@ struct dentry *mount_bdev(struct file_system_type *fs_type, error_s: error = PTR_ERR(s); +error_inc: + if (current_user_ns() != &init_user_ns) { + atomic_inc(&bdev->bd_inode->i_writecount); + if (bdev->bd_contains != bdev) + atomic_inc(&bdev->bd_contains->bd_inode->i_writecount); + } error_bdev: blkdev_put(bdev, mode); error: @@ -1101,6 +1124,11 @@ void kill_block_super(struct super_block *sb) generic_shutdown_super(sb); sync_blockdev(bdev); WARN_ON_ONCE(!(mode & FMODE_EXCL)); + if (sb->s_user_ns != &init_user_ns) { + atomic_inc(&bdev->bd_inode->i_writecount); + if (bdev->bd_contains != bdev) + atomic_inc(&bdev->bd_contains->bd_inode->i_writecount); + } blkdev_put(bdev, mode | FMODE_EXCL); }