From patchwork Thu Jun 22 18:59:48 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 9805125 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 40D3160386 for ; Thu, 22 Jun 2017 19:00:23 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2F43B28724 for ; Thu, 22 Jun 2017 19:00:23 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2250B28737; Thu, 22 Jun 2017 19:00:23 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E40DD28724 for ; Thu, 22 Jun 2017 19:00:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753750AbdFVTAG (ORCPT ); Thu, 22 Jun 2017 15:00:06 -0400 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:50532 "EHLO mx0a-001b2d01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752392AbdFVTAF (ORCPT ); Thu, 22 Jun 2017 15:00:05 -0400 Received: from pps.filterd (m0098393.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v5MIwklB126198 for ; Thu, 22 Jun 2017 15:00:05 -0400 Received: from e13.ny.us.ibm.com (e13.ny.us.ibm.com [129.33.205.203]) by mx0a-001b2d01.pphosted.com with ESMTP id 2b8eh0ymjt-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 22 Jun 2017 15:00:04 -0400 Received: from localhost by e13.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 22 Jun 2017 15:00:03 -0400 Received: from b01cxnp23034.gho.pok.ibm.com (9.57.198.29) by e13.ny.us.ibm.com (146.89.104.200) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 22 Jun 2017 15:00:00 -0400 Received: from b01ledav001.gho.pok.ibm.com (b01ledav001.gho.pok.ibm.com [9.57.199.106]) by b01cxnp23034.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v5MIxxKD23396538; Thu, 22 Jun 2017 18:59:59 GMT Received: from b01ledav001.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 95E4D2803D; Thu, 22 Jun 2017 14:59:53 -0400 (EDT) Received: from sbct-3.watson.ibm.com (unknown [9.2.141.158]) by b01ledav001.gho.pok.ibm.com (Postfix) with ESMTP id 571992803F; Thu, 22 Jun 2017 14:59:53 -0400 (EDT) From: Stefan Berger To: ebiederm@xmission.com, containers@lists.linux-foundation.org Cc: lkp@01.org, xiaolong.ye@intel.com, linux-kernel@vger.kernel.org, zohar@linux.vnet.ibm.com, serge@hallyn.com, tycho@docker.com, James.Bottomley@HansenPartnership.com, christian.brauner@mailbox.org, stefanb@linux.vnet.ibm.com, vgoyal@redhat.com, amir73il@gmail.com, linux-security-module@vger.kernel.org Subject: [PATCH 2/3] Enable capabilities of files from shared filesystem Date: Thu, 22 Jun 2017 14:59:48 -0400 X-Mailer: git-send-email 2.5.5 In-Reply-To: <1498157989-11814-1-git-send-email-stefanb@linux.vnet.ibm.com> References: <1498157989-11814-1-git-send-email-stefanb@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17062219-0008-0000-0000-0000024AA58D X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00007273; HX=3.00000241; KW=3.00000007; PH=3.00000004; SC=3.00000214; SDB=6.00878407; UDB=6.00437683; IPR=6.00658543; BA=6.00005437; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00015926; XFM=3.00000015; UTC=2017-06-22 19:00:03 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17062219-0009-0000-0000-000035C39CA2 Message-Id: <1498157989-11814-3-git-send-email-stefanb@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-06-22_08:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=2 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1703280000 definitions=main-1706220324 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP The previous patch changed existing behavior in so far as the capabilities of files from a shared filesystem or bind-mounted files were hidden from a user namespace. This patch makes these capabilties visible to the user namespace again, unless the user namespace has set its own capabilities, which will hide them until those set by the user namespace are removed. Also the listing of xattrs is adjusted. To avoid double listing of names of extended attributes, which can happen if the container and the host for example have security.capability, we now check that we do not add the same extended attribute to the list twice. Signed-off-by: Stefan Berger Signed-off-by: Serge Hallyn Reviewed-by: Serge Hallyn --- fs/xattr.c | 90 ++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/fs/xattr.c b/fs/xattr.c index 64c4b40..045be85 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -339,6 +339,26 @@ xattr_rewrite_userns_xattr(char *name) } /* + * xattr_list_contains - check whether an xattr list already contains a needle + * + * @list : 0-byte separated strings + * @listlen : length of the list + * @needle : the needle to search for + */ +static int +xattr_list_contains(const char *list, size_t listlen, const char *needle) +{ + size_t o = 0; + + while (o < listlen) { + if (!strcmp(&list[o], needle)) + return true; + o += strlen(&list[o]) + 1; + } + return false; +} + +/* * xattr_list_userns_rewrite - Rewrite list of xattr names for user namespaces * or determine needed size for attribute list * in case size == 0 @@ -377,12 +397,16 @@ xattr_list_userns_rewrite(char *list, ssize_t size, size_t list_maxlen) if (!len) break; - newname = xattr_rewrite_userns_xattr(name); - if (IS_ERR(newname)) { - d_off = PTR_ERR(newname); - goto out_free; + if (xattr_is_userns_supported(name, false) >= 0) + newname = name; + else { + newname = xattr_rewrite_userns_xattr(name); + if (IS_ERR(newname)) { + d_off = PTR_ERR(newname); + goto out_free; + } } - if (newname) { + if (newname && !xattr_list_contains(nlist, d_off, newname)) { nlen = strlen(newname); if (nlist) { @@ -413,13 +437,19 @@ xattr_list_userns_rewrite(char *list, ssize_t size, size_t list_maxlen) * security.foo to protect these extended attributes. * * Reading: - * 1) Reading security.foo from a user namespace will read - * security.foo@uid= of the parent user namespace instead with uid - * being the mapping of root in that parent user namespace. An - * exception is if root is mapped to uid 0 on the host, and in this case - * we will read security.foo directly. - * -> reading security.foo will read security.foo@uid=1000 for a uid - * mapping of root to 1000. + * 1a) Reading security.foo from a user namespace will read + * security.foo@uid= of the parent user namespace instead with uid + * being the mapping of root in that parent user namespace. An + * exception is if root is mapped to uid 0 on the host, and in this case + * we will read security.foo directly. + * -> reading security.foo will read security.foo@uid=1000 for a uid + * mapping of root to 1000. + * + * 1b) If security.foo@uid= is not available, the security.foo of the + * parent namespace is tried to be read. This procedure is repeated up to + * the init user namespace. This step only applies for reading of extended + * attributes and provides the same behavior as older systems where the + * host's extended attributes applied to user namespaces. * * 2) All security.foo@uid= with valid uid mappings in the user namespace * an be read. The uid within the user namespace will be mapped to the @@ -434,7 +464,7 @@ xattr_list_userns_rewrite(char *list, ssize_t size, size_t list_maxlen) * 3) No other security.foo* can be read. * * Writing and removing: - * The same rules for reading apply to writing and removing. + * The same rules for reading apply to writing and removing, except for 1b). * * This function returns a buffer with either the original name or the * user namespace adjusted name of the extended attribute. @@ -444,11 +474,12 @@ xattr_list_userns_rewrite(char *list, ssize_t size, size_t list_maxlen) * @is_write: whether this is for writing an xattr */ char * -xattr_userns_name(const char *fullname, const char *suffix) +xattr_userns_name(const char *fullname, const char *suffix, + struct user_namespace *userns) { size_t buflen; char *buffer, *ptr, *n_uidstr; - kuid_t root_uid = make_kuid(current_user_ns(), 0); + kuid_t root_uid = make_kuid(userns, 0); int idx, error; size_t len = 0, slen; @@ -467,7 +498,7 @@ xattr_userns_name(const char *fullname, const char *suffix) * init_user_ns or userns with root mapped to uid 0 * may read security.foo directly */ - if (current_user_ns() == &init_user_ns || + if (userns == &init_user_ns || __kuid_val(root_uid) == 0) goto out_copy; @@ -485,7 +516,7 @@ xattr_userns_name(const char *fullname, const char *suffix) * We must have fullname[len] == '@'. */ error = xattr_parse_uid_make_kuid(&fullname[len], - current_user_ns(), + userns, &n_uidstr); if (error) return ERR_PTR(error); @@ -540,7 +571,7 @@ __vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name, return -EOPNOTSUPP; if (size == 0) value = ""; /* empty EA, do not remove */ - nsuffix = xattr_userns_name(fullname, name); + nsuffix = xattr_userns_name(fullname, name, current_user_ns()); if (IS_ERR(nsuffix)) return PTR_ERR(nsuffix); ret = handler->set(handler, dentry, inode, nsuffix, value, size, flags); @@ -703,18 +734,24 @@ __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name, const struct xattr_handler *handler; char *nsuffix; const char *fullname = name; - int ret; + int ret, userns_supt_xattr; + struct user_namespace *userns = current_user_ns(); handler = xattr_resolve_name(inode, &name); if (IS_ERR(handler)) return PTR_ERR(handler); if (!handler->get) return -EOPNOTSUPP; - nsuffix = xattr_userns_name(fullname, name); - if (IS_ERR(nsuffix)) - return PTR_ERR(nsuffix); - ret = handler->get(handler, dentry, inode, nsuffix, value, size); - kfree(nsuffix); + userns_supt_xattr = (xattr_is_userns_supported(fullname, false) >= 0); + do { + nsuffix = xattr_userns_name(fullname, name, userns); + if (IS_ERR(nsuffix)) + return PTR_ERR(nsuffix); + ret = handler->get(handler, dentry, inode, nsuffix, value, + size); + kfree(nsuffix); + userns = userns->parent; + } while ((ret == -ENODATA) && userns && userns_supt_xattr); return ret; } EXPORT_SYMBOL(__vfs_getxattr); @@ -737,7 +774,8 @@ vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) XATTR_SECURITY_PREFIX_LEN)) { int ret; const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; - char *nsuffix = xattr_userns_name(name, suffix); + char *nsuffix = xattr_userns_name(name, suffix, + current_user_ns()); if (IS_ERR(nsuffix)) return PTR_ERR(nsuffix); @@ -794,7 +832,7 @@ __vfs_removexattr(struct dentry *dentry, const char *name) return PTR_ERR(handler); if (!handler->set) return -EOPNOTSUPP; - nsuffix = xattr_userns_name(fullname, name); + nsuffix = xattr_userns_name(fullname, name, current_user_ns()); if (IS_ERR(nsuffix)) return PTR_ERR(nsuffix); ret = handler->set(handler, dentry, inode, nsuffix, NULL, 0,