Received: by 2002:ac0:a594:0:0:0:0:0 with SMTP id m20-v6csp2954176imm; Thu, 24 May 2018 19:49:08 -0700 (PDT) X-Google-Smtp-Source: AB8JxZpXhZssXXx4rLxmdUZMIt9zV6BdyIoQ2fws4i4EtgOj6GVS5BLLfQ/x3BniBisTipEuPm72 X-Received: by 2002:a62:4086:: with SMTP id f6-v6mr610390pfd.194.1527216548071; Thu, 24 May 2018 19:49:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527216548; cv=none; d=google.com; s=arc-20160816; b=V1tbYu4X1panbKi1F0Sv3MtE4pXuvCKlSfyBTprazEjMLC9tyDNaySTGycFrNT2Sm8 F6OgQcH9WSyiXBGQiI1zWzB9yJJmpm1xszXJcBwJZjtt3qkz0+juJz6ClVtN4LI4HqCm DA5pZw4htuF5oENIPD6jQ6QyVGf8qJq+CtpsW/P6yrDAqc8qABdHQzNRb/czuIMTcs7T z2RQW6ixaavDe0dQ4ii1ZtLQDf7VhAR6dtgPoo26nov9Vj2lQCdKOv5xBQEDKnSLSlKA EFUAG2gmeORAeH8iaUC3+gme6xEEYo/Au86U4G8X5AgXA8hYChvIebnTq4na19Y+tBkE AfWA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:cc:to:from :subject:organization:arc-authentication-results; bh=m3EQe3pszk8LGgnR4yL1C/TutdbTdqz6+lp1BPSgROU=; b=IwtyQNe+jBOV/fnc35Qql6xZcOdev05TnA0EY20F4ItgUYVtuCSCm475EXlbDl47oO 0Q2UJtX1QPvtQNRe5+vF5BN1JClD6cIZbnPfQlKoPdS1fEnX2PzDbZ7OKn5FHGcDV4zH 5fvblKqAGs/YegzVbS8NmohvgVAc531YG6hlMX1c2oRsby+gg3QsHWa3ScoPi1LB/uyn sLorHNr/z+Nf53VYQERGIFIR0i1btv30ci/9aFvvtwdLzsmzfRmHCJ5JbKlMao0/K+t+ gJj1OF3TlVJbV81bNNs7XhjgNY34Utb/g9fC+6xNoe5B6l/tc/m7yXtbDbDkjeu5tg+i wqWA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id u191-v6si2295774pgd.667.2018.05.24.19.48.53; Thu, 24 May 2018 19:49:08 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S970167AbeEYAOG (ORCPT + 99 others); Thu, 24 May 2018 20:14:06 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:60428 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S971672AbeEYAF6 (ORCPT ); Thu, 24 May 2018 20:05:58 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C380240137F3; Fri, 25 May 2018 00:05:57 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-120-255.rdu2.redhat.com [10.10.120.255]) by smtp.corp.redhat.com (Postfix) with ESMTP id 09E50946AF; Fri, 25 May 2018 00:05:56 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 05/32] selinux: Implement the new mount API LSM hooks [ver #8] From: David Howells To: viro@zeniv.linux.org.uk Cc: dhowells@redhat.com, linux-fsdevel@vger.kernel.org, linux-afs@lists.infradead.org, linux-kernel@vger.kernel.org Date: Fri, 25 May 2018 01:05:56 +0100 Message-ID: <152720675642.9073.4588309640055055707.stgit@warthog.procyon.org.uk> In-Reply-To: <152720672288.9073.9868393448836301272.stgit@warthog.procyon.org.uk> References: <152720672288.9073.9868393448836301272.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Fri, 25 May 2018 00:05:57 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Fri, 25 May 2018 00:05:57 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'dhowells@redhat.com' RCPT:'' Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement the new mount API LSM hooks for SELinux. At some point the old hooks will need to be removed. Question: Should the ->fs_context_parse_source() hook be implemented to check the labels on any source devices specified? Signed-off-by: David Howells cc: Paul Moore cc: Stephen Smalley cc: selinux@tycho.nsa.gov cc: linux-security-module@vger.kernel.org --- security/selinux/hooks.c | 262 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 54ecb1c18ca1..1ab74c5ae789 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -2971,6 +2972,259 @@ static int selinux_umount(struct vfsmount *mnt, int flags) FILESYSTEM__UNMOUNT, NULL); } +/* fsopen mount context operations */ + +static int selinux_fs_context_alloc(struct fs_context *fc, + struct dentry *reference) +{ + struct security_mnt_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return -ENOMEM; + + fc->security = opts; + return 0; +} + +static int selinux_fs_context_dup(struct fs_context *fc, + struct fs_context *src_fc) +{ + const struct security_mnt_opts *src = src_fc->security; + struct security_mnt_opts *opts; + int i, n; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return -ENOMEM; + fc->security = opts; + + if (!src || !src->num_mnt_opts) + return 0; + n = opts->num_mnt_opts = src->num_mnt_opts; + + if (src->mnt_opts) { + opts->mnt_opts = kcalloc(n, sizeof(char *), GFP_KERNEL); + if (!opts->mnt_opts) + return -ENOMEM; + + for (i = 0; i < n; i++) { + if (src->mnt_opts[i]) { + opts->mnt_opts[i] = kstrdup(src->mnt_opts[i], + GFP_KERNEL); + if (!opts->mnt_opts[i]) + return -ENOMEM; + } + } + } + + if (src->mnt_opts_flags) { + opts->mnt_opts_flags = kmemdup(src->mnt_opts_flags, + n * sizeof(int), GFP_KERNEL); + if (!opts->mnt_opts_flags) + return -ENOMEM; + } + + return 0; +} + +static void selinux_fs_context_free(struct fs_context *fc) +{ + struct security_mnt_opts *opts = fc->security; + + security_free_mnt_opts(opts); + fc->security = NULL; +} + +static int selinux_fs_context_parse_option(struct fs_context *fc, char *opt, size_t len) +{ + struct security_mnt_opts *opts = fc->security; + substring_t args[MAX_OPT_ARGS]; + unsigned int have; + char *c, **oo; + int token, ctx, i, *of; + + token = match_token(opt, tokens, args); + if (token == Opt_error) + return 0; /* Doesn't belong to us. */ + + have = 0; + for (i = 0; i < opts->num_mnt_opts; i++) + have |= 1 << opts->mnt_opts_flags[i]; + if (have & (1 << token)) + return -EINVAL; + + switch (token) { + case Opt_context: + if (have & (1 << Opt_defcontext)) + goto incompatible; + ctx = CONTEXT_MNT; + goto copy_context_string; + + case Opt_fscontext: + ctx = FSCONTEXT_MNT; + goto copy_context_string; + + case Opt_rootcontext: + ctx = ROOTCONTEXT_MNT; + goto copy_context_string; + + case Opt_defcontext: + if (have & (1 << Opt_context)) + goto incompatible; + ctx = DEFCONTEXT_MNT; + goto copy_context_string; + + case Opt_labelsupport: + return 1; + + default: + return -EINVAL; + } + +copy_context_string: + if (opts->num_mnt_opts > 3) + return -EINVAL; + + of = krealloc(opts->mnt_opts_flags, + (opts->num_mnt_opts + 1) * sizeof(int), GFP_KERNEL); + if (!of) + return -ENOMEM; + of[opts->num_mnt_opts] = 0; + opts->mnt_opts_flags = of; + + oo = krealloc(opts->mnt_opts, + (opts->num_mnt_opts + 1) * sizeof(char *), GFP_KERNEL); + if (!oo) + return -ENOMEM; + oo[opts->num_mnt_opts] = NULL; + opts->mnt_opts = oo; + + c = match_strdup(&args[0]); + if (!c) + return -ENOMEM; + opts->mnt_opts[opts->num_mnt_opts] = c; + opts->mnt_opts_flags[opts->num_mnt_opts] = ctx; + opts->num_mnt_opts++; + return 1; + +incompatible: + return -EINVAL; +} + +/* + * Validate the security parameters supplied for a reconfiguration/remount + * event. + */ +static int selinux_validate_for_sb_reconfigure(struct fs_context *fc) +{ + struct super_block *sb = fc->root->d_sb; + struct superblock_security_struct *sbsec = sb->s_security; + struct security_mnt_opts *opts = fc->security; + int rc, i, *flags; + char **mount_options; + + if (!(sbsec->flags & SE_SBINITIALIZED)) + return 0; + + mount_options = opts->mnt_opts; + flags = opts->mnt_opts_flags; + + for (i = 0; i < opts->num_mnt_opts; i++) { + u32 sid; + + if (flags[i] == SBLABEL_MNT) + continue; + + rc = security_context_str_to_sid(&selinux_state, mount_options[i], + &sid, GFP_KERNEL); + if (rc) { + pr_warn("SELinux: security_context_str_to_sid" + "(%s) failed for (dev %s, type %s) errno=%d\n", + mount_options[i], sb->s_id, sb->s_type->name, rc); + goto inval; + } + + switch (flags[i]) { + case FSCONTEXT_MNT: + if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid)) + goto bad_option; + break; + case CONTEXT_MNT: + if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid)) + goto bad_option; + break; + case ROOTCONTEXT_MNT: { + struct inode_security_struct *root_isec; + root_isec = backing_inode_security(sb->s_root); + + if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid)) + goto bad_option; + break; + } + case DEFCONTEXT_MNT: + if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid)) + goto bad_option; + break; + default: + goto inval; + } + } + + rc = 0; +out: + return rc; + +bad_option: + pr_warn("SELinux: unable to change security options " + "during remount (dev %s, type=%s)\n", + sb->s_id, sb->s_type->name); +inval: + rc = -EINVAL; + goto out; +} + +/* + * Validate the security context assembled from the option data supplied to + * mount. + */ +static int selinux_fs_context_validate(struct fs_context *fc) +{ + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) + return selinux_validate_for_sb_reconfigure(fc); + return 0; +} + +/* + * Set the security context on a superblock. + */ +static int selinux_sb_get_tree(struct fs_context *fc) +{ + const struct cred *cred = current_cred(); + struct common_audit_data ad; + int rc; + + rc = selinux_set_mnt_opts(fc->root->d_sb, fc->security, 0, NULL); + if (rc) + return rc; + + /* Allow all mounts performed by the kernel */ + if (fc->purpose == FS_CONTEXT_FOR_KERNEL_MOUNT) + return 0; + + ad.type = LSM_AUDIT_DATA_DENTRY; + ad.u.dentry = fc->root; + return superblock_has_perm(cred, fc->root->d_sb, FILESYSTEM__MOUNT, &ad); +} + +static int selinux_sb_mountpoint(struct fs_context *fc, struct path *mountpoint, + unsigned int mnt_flags) +{ + const struct cred *cred = current_cred(); + + return path_has_perm(cred, mountpoint, FILE__MOUNTON); +} + /* inode security operations */ static int selinux_inode_alloc_security(struct inode *inode) @@ -6882,6 +7136,14 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds), LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds), + LSM_HOOK_INIT(fs_context_alloc, selinux_fs_context_alloc), + LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup), + LSM_HOOK_INIT(fs_context_free, selinux_fs_context_free), + LSM_HOOK_INIT(fs_context_parse_option, selinux_fs_context_parse_option), + LSM_HOOK_INIT(fs_context_validate, selinux_fs_context_validate), + LSM_HOOK_INIT(sb_get_tree, selinux_sb_get_tree), + LSM_HOOK_INIT(sb_mountpoint, selinux_sb_mountpoint), + LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security), LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security), LSM_HOOK_INIT(sb_copy_data, selinux_sb_copy_data),