From patchwork Wed May 4 14:26:52 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Djalal Harouni X-Patchwork-Id: 9014651 Return-Path: X-Original-To: patchwork-linux-fsdevel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 490CA9F39D for ; Wed, 4 May 2016 14:30:54 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9EE5B203A5 for ; Wed, 4 May 2016 14:30:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 77CF3203A1 for ; Wed, 4 May 2016 14:30:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753262AbcEDOaD (ORCPT ); Wed, 4 May 2016 10:30:03 -0400 Received: from mail-wm0-f47.google.com ([74.125.82.47]:37072 "EHLO mail-wm0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753095AbcEDO3v (ORCPT ); Wed, 4 May 2016 10:29:51 -0400 Received: by mail-wm0-f47.google.com with SMTP id a17so97735080wme.0; Wed, 04 May 2016 07:29:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=qPv936/EiK/Aq6Cpd3LLm/i8TssVMivzu2bXYLiw40o=; b=qVJVWzeMNSY6z1wOWW3IjBC/aCoBrQkB+XmA4tPbZ/pcIb0npT8BHZoiiRjQD8DZUI quHmGxaSewL0djkovzBdxAyeorFTb0kIEx0uQzc43+Z4JwBmR18mp/oeuFTLyHFuFhNo Ayg5EXrnhovtD9nXSAgZUBS4YOBFNHTCPzUBb4gNPLuf8GBj0+dff0i9Kof+9O90XAX+ Z1vvUZwsaIhjZwR3qQf5B+OFEp9/zU/H9cexqGq9NolDPZd2BZP/QFGCt12LF/mMcW1d 5IqFAOYrNvHUO3vAtyEu4hVW0y8vHvNF4qfUqd/BjvOUSqC+3dAw1mIT2TgmWuY61Z6U C9Kg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=qPv936/EiK/Aq6Cpd3LLm/i8TssVMivzu2bXYLiw40o=; b=a3sIg97tZ3xm44j5+o9I7Fs8IpacEXUZtaggWV+3EIhjvzaGoUHORshCeCh4RsM42S vB6PbTrCHtdv4aAf4JT8bURXgcHb3fuoQQr0nNqgTgCj1QvthhBt0FZnAy9SFdhnGK2f dvC0CcRKciGrSGcvQYlSW1Jwy+WtNKi6SzdxryFURv9S6viGbPKRllfWDHnF4bLArIwI K5yQ5VEUyPm84LicEr4Uix72HM/PesYm864XKRoxm3WwjKrbfIGw2Ez4LPCEi1C+PFcF zz7/xeZSg60tIT2qrd4c5Xb4nP0xVdcQ9h6pMW4F/n7P1E2Ti0shH3vWcBLM9jy0M1g1 oTLw== X-Gm-Message-State: AOPr4FXveejFxUhPHgB1qkaN03Hea6MCdH0gegusFjtdOHBPAd6LB/TiDDwnYhNBHlJR0Q== X-Received: by 10.28.27.142 with SMTP id b136mr31602515wmb.7.1462372189266; Wed, 04 May 2016 07:29:49 -0700 (PDT) Received: from dztty2.localdomain (ip5b42f9c9.dynamic.kabel-deutschland.de. [91.66.249.201]) by smtp.gmail.com with ESMTPSA id a75sm4615505wme.18.2016.05.04.07.29.47 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 May 2016 07:29:48 -0700 (PDT) From: Djalal Harouni To: Alexander Viro , Chris Mason , , Serge Hallyn , Josh Triplett , "Eric W. Biederman" , Andy Lutomirski , Seth Forshee , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, Dongsu Park , David Herrmann , Miklos Szeredi , Alban Crequy Cc: Djalal Harouni , Djalal Harouni Subject: [RFC v2 PATCH 6/8] VFS:userns: shift UID/GID to on-disk view before any write to disk Date: Wed, 4 May 2016 16:26:52 +0200 Message-Id: <1462372014-3786-7-git-send-email-tixxdz@gmail.com> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1462372014-3786-1-git-send-email-tixxdz@gmail.com> References: <1462372014-3786-1-git-send-email-tixxdz@gmail.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Spam-Status: No, score=-8.9 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP If both the mount namespace and the mount point support UID/GID shifts, then during inode creation or during a chown call on an inode, make sure that kuid and kgid that will be used to set inode->{i_uid|i_gid} are in on-disk view. Perform the shift to on-disk view during inode initialization or during notify_change() calls. Usually in this case inode's uid/gid will contain a kuid and kgid that are valid in the context of the caller and its view inside the global init_user_ns user namespace. They will always end up either with current_fsuid() value or the attr->ia_uid of the struct iattr. inode->{i_uid|i_gid} on-disk writes inside user_ns_X ---------------------------------------------------- Without this Patch: ------------------------------------------------------------ user_ns_X uid | init_user_ns uid | inode->i_uid on-disk ------------------------------------------------------------ 0 | 1000000 | 1000000 ------------------------------------------------------------ 999 | 1000999 | 1000999 ------------------------------------------------------------ 1000 | 1001000 | 1001000 ------------------------------------------------------------ inode->{i_uid|i_gid} always end up with global kuid/kgid of the caller in the init_user_ns. With this patch: ------------------------------------------------------------ user_ns_X uid | init_user_ns uid | inode->i_uid on-disk ------------------------------------------------------------ 0 | 1000000 | 0 ------------------------------------------------------------ 999 | 1000999 | 999 ------------------------------------------------------------ 1000 | 1001000 | 1000 ------------------------------------------------------------ inode->{i_uid|i_gid} will have the values of the uid_t and gid_t that are shown inside the user namespace of the caller. Of course this works only on mounts that support VFS UID/GID shift and are inside a mount namespace that also supports the above. The shift into on-disk is done inside notify_change() to give a chance to notify_change_ok() to catch permissions access. At the same time we adapt notify_change_ok() and make the necessary translation when it's needed from virtual ot on-disk and vice versa. The approach is to always keep inode->{i_uid|i_gid} even in memory with on-disk values. The virtual translation is only done when needed for permission access or stat() calls. Signed-off-by: Dongsu Park Signed-off-by: Djalal Harouni --- fs/attr.c | 44 +++++++++++++++++++++++++++++++++----------- fs/inode.c | 4 ++-- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index 25b24d0..c476257 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -47,26 +47,38 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr) return 0; /* Make sure a caller can chown. */ - if ((ia_valid & ATTR_UID) && - (!uid_eq(current_fsuid(), inode->i_uid) || - !uid_eq(attr->ia_uid, inode->i_uid)) && - !capable_wrt_inode_uidgid(inode, CAP_CHOWN)) - return -EPERM; + if (ia_valid & ATTR_UID) { + /* Shift to virtual if necessary */ + kuid_t i_uid = vfs_shift_i_uid_to_virtual(inode); + + if ((!uid_eq(current_fsuid(), i_uid) || + !uid_eq(attr->ia_uid, inode->i_uid)) && + !capable_wrt_inode_uidgid(inode, CAP_CHOWN)) + return -EPERM; + } /* Make sure caller can chgrp. */ - if ((ia_valid & ATTR_GID) && - (!uid_eq(current_fsuid(), inode->i_uid) || - (!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) && - !capable_wrt_inode_uidgid(inode, CAP_CHOWN)) + if (ia_valid & ATTR_GID) { + /* Shift to virtual if ncessary */ + kuid_t i_uid = vfs_shift_i_uid_to_virtual(inode); + /* Shift it back to virtual if necessary */ + kgid_t ia_gid = vfs_kgid_disk_to_virtual(inode, attr->ia_gid); + + if ((!uid_eq(current_fsuid(), i_uid) || + (!in_group_p(ia_gid) && + !gid_eq(attr->ia_gid, inode->i_gid))) && + !capable_wrt_inode_uidgid(inode, CAP_CHOWN)) return -EPERM; + } /* Make sure a caller can chmod. */ if (ia_valid & ATTR_MODE) { if (!inode_owner_or_capable(inode)) return -EPERM; /* Also check the setgid bit! */ - if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : - inode->i_gid) && + if (!in_group_p((ia_valid & ATTR_GID) ? + vfs_kgid_disk_to_virtual(inode, attr->ia_gid) : + vfs_shift_i_gid_to_virtual(inode)) && !capable_wrt_inode_uidgid(inode, CAP_FSETID)) attr->ia_mode &= ~S_ISGID; } @@ -209,6 +221,16 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de inode->i_flags &= ~S_NOSEC; } + /* + * Shift if necessary the UID and GID that are mean to be written + * into inodes's uid/gid to on-disk view. Do that as early as + * possible. + */ + if ((ia_valid & ATTR_UID)) + attr->ia_uid = vfs_shift_kuid_to_disk(inode, attr->ia_uid); + if ((ia_valid & ATTR_GID)) + attr->ia_gid = vfs_shift_kgid_to_disk(inode, attr->ia_gid); + now = current_fs_time(inode->i_sb); attr->ia_ctime = now; diff --git a/fs/inode.c b/fs/inode.c index 07daf5f..e6ee56a 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1940,13 +1940,13 @@ EXPORT_SYMBOL(init_special_inode); void inode_init_owner(struct inode *inode, const struct inode *dir, umode_t mode) { - inode->i_uid = current_fsuid(); + inode->i_uid = vfs_shift_kuid_to_disk(inode, current_fsuid()); if (dir && dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; } else - inode->i_gid = current_fsgid(); + inode->i_gid = vfs_shift_kgid_to_disk(inode, current_fsgid()); inode->i_mode = mode; } EXPORT_SYMBOL(inode_init_owner);