From patchwork Thu Feb 27 21:11:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Simmons X-Patchwork-Id: 11410021 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 543851580 for ; Thu, 27 Feb 2020 21:28:06 +0000 (UTC) Received: from pdx1-mailman02.dreamhost.com (pdx1-mailman02.dreamhost.com [64.90.62.194]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 3C92F246A0 for ; Thu, 27 Feb 2020 21:28:06 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 3C92F246A0 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=infradead.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lustre-devel-bounces@lists.lustre.org Received: from pdx1-mailman02.dreamhost.com (localhost [IPv6:::1]) by pdx1-mailman02.dreamhost.com (Postfix) with ESMTP id 83391349281; Thu, 27 Feb 2020 13:24:36 -0800 (PST) X-Original-To: lustre-devel@lists.lustre.org Delivered-To: lustre-devel-lustre.org@pdx1-mailman02.dreamhost.com Received: from smtp3.ccs.ornl.gov (smtp3.ccs.ornl.gov [160.91.203.39]) by pdx1-mailman02.dreamhost.com (Postfix) with ESMTP id 82BC921FBD9 for ; Thu, 27 Feb 2020 13:19:19 -0800 (PST) Received: from star.ccs.ornl.gov (star.ccs.ornl.gov [160.91.202.134]) by smtp3.ccs.ornl.gov (Postfix) with ESMTP id A6AFE2AD4; Thu, 27 Feb 2020 16:18:15 -0500 (EST) Received: by star.ccs.ornl.gov (Postfix, from userid 2004) id A4DF146A; Thu, 27 Feb 2020 16:18:15 -0500 (EST) From: James Simmons To: Andreas Dilger , Oleg Drokin , NeilBrown Date: Thu, 27 Feb 2020 16:11:09 -0500 Message-Id: <1582838290-17243-202-git-send-email-jsimmons@infradead.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1582838290-17243-1-git-send-email-jsimmons@infradead.org> References: <1582838290-17243-1-git-send-email-jsimmons@infradead.org> Subject: [lustre-devel] [PATCH 201/622] lustre: sec: create new function sptlrpc_get_sepol() X-BeenThere: lustre-devel@lists.lustre.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "For discussing Lustre software development." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Lustre Development List MIME-Version: 1.0 Errors-To: lustre-devel-bounces@lists.lustre.org Sender: "lustre-devel" From: Sebastien Buisson Create new function sptlrpc_get_sepol() in ptlrpc/sec.c to compute SELinux policy info, by calling new userland command l_getsepol. The SELinux policy info syntax is the following: ::: where: - is a digit telling if SELinux is in Permissive mode (0) or Enforcing mode (1) - is the name of the SELinux policy - is the version of the SELinux policy - is the computed hash of the binary representation of the policy, as exported in /etc/selinux//policy/policy. Userland command l_getsepol can be called on the command line by a security administrator to get SELinux status information to store into 'sepol' field of nodemap. SELinux status information is reported by Lustre client only if new 'send_sepol' ptlrpc kernel module's parameter is not zero, and SELinux is enabled on the client. 'send_sepol' accepts various values: - 0: do not send SELinux policy info; - -1: send SELinux policy info for every request; - N > 0: only send SELinux policy info every N seconds. Use max value 2^31-1 (signed int on 32 bits) to make sure SELinux policy info is only checked at mount time. Independently from 'send_sepol' value, SELinux policy info has an associated mtime. l_getsepol checks mtime and recalculates whole SELinux policy info (including SHA) only if mtime changed. WC-bug-id: https://jira.whamcloud.com/browse/LU-8955 Lustre-commit: c61168239eff ("LU-8955 sec: create new function sptlrpc_get_sepol()") Signed-off-by: Sebastien Buisson Reviewed-on: https://review.whamcloud.com/24421 Reviewed-by: Patrick Farrell Reviewed-by: Li Dongyang Reviewed-by: Oleg Drokin Signed-off-by: James Simmons --- fs/lustre/include/lustre_net.h | 7 ++ fs/lustre/include/lustre_sec.h | 12 +++ fs/lustre/ptlrpc/sec.c | 125 ++++++++++++++++++++++++++++++++ fs/lustre/ptlrpc/sec_lproc.c | 74 +++++++++++++++++++ include/uapi/linux/lustre/lustre_idl.h | 13 ++++ include/uapi/linux/lustre/lustre_user.h | 9 +++ 6 files changed, 240 insertions(+) diff --git a/fs/lustre/include/lustre_net.h b/fs/lustre/include/lustre_net.h index 81a6ac9..36de665 100644 --- a/fs/lustre/include/lustre_net.h +++ b/fs/lustre/include/lustre_net.h @@ -845,6 +845,13 @@ struct ptlrpc_request { /** description of flavors for client & server */ struct sptlrpc_flavor rq_flvr; + /** + * SELinux policy info at the time of the request + * sepol string format is: + * ::: + */ + char rq_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1]; + /* client/server security flags */ unsigned int rq_ctx_init:1, /* context initiation */ diff --git a/fs/lustre/include/lustre_sec.h b/fs/lustre/include/lustre_sec.h index 99702fd..00710d6 100644 --- a/fs/lustre/include/lustre_sec.h +++ b/fs/lustre/include/lustre_sec.h @@ -792,6 +792,17 @@ struct ptlrpc_sec { /** owning import */ struct obd_import *ps_import; spinlock_t ps_lock; + /** mtime of SELinux policy file */ + time_t ps_sepol_mtime; + /** next check time of SELinux policy file */ + ktime_t ps_sepol_checknext; + /** + * SELinux policy info + * sepol string format is: + * ::: + */ + char ps_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + + 1]; /* * garbage collection @@ -987,6 +998,7 @@ int sptlrpc_cli_unwrap_early_reply(struct ptlrpc_request *req, void sptlrpc_cli_finish_early_reply(struct ptlrpc_request *early_req); void sptlrpc_request_out_callback(struct ptlrpc_request *req); +int sptlrpc_get_sepol(struct ptlrpc_request *req); /* * exported higher interface of import & request diff --git a/fs/lustre/ptlrpc/sec.c b/fs/lustre/ptlrpc/sec.c index 54ca97c..789b5cb 100644 --- a/fs/lustre/ptlrpc/sec.c +++ b/fs/lustre/ptlrpc/sec.c @@ -53,6 +53,10 @@ #include "ptlrpc_internal.h" +static int send_sepol; +module_param(send_sepol, int, 0644); +MODULE_PARM_DESC(send_sepol, "Client sends SELinux policy status"); + /*********************************************** * policy registers * ***********************************************/ @@ -1692,6 +1696,127 @@ static int sptlrpc_svc_install_rvs_ctx(struct obd_import *imp, return policy->sp_sops->install_rctx(imp, ctx); } +#ifdef CONFIG_SECURITY_SELINUX +/* Get SELinux policy info from userspace */ +static int sepol_helper(struct obd_import *imp) +{ + char mtime_str[21] = { 0 }, mode_str[2] = { 0 }; + char *argv[] = { + [0] = "/usr/sbin/l_getsepol", + [1] = "-o", + [2] = NULL, /* obd type */ + [3] = "-n", + [4] = NULL, /* obd name */ + [5] = "-t", + [6] = mtime_str, /* policy mtime */ + [7] = "-m", + [8] = mode_str, /* enforcing mode */ + [9] = NULL + }; + static char *envp[] = { + [0] = "HOME=/", + [1] = "PATH=/sbin:/usr/sbin", + [2] = NULL + }; + signed short ret; + int rc = 0; + + if (!imp || !imp->imp_obd || + !imp->imp_obd->obd_type) { + rc = -EINVAL; + } else { + argv[2] = (char *)imp->imp_obd->obd_type->typ_name; + argv[4] = imp->imp_obd->obd_name; + spin_lock(&imp->imp_sec->ps_lock); + if (imp->imp_sec->ps_sepol_mtime == 0 && + imp->imp_sec->ps_sepol[0] == '\0') { + /* ps_sepol has not been initialized */ + argv[5] = NULL; + argv[7] = NULL; + } else { + snprintf(mtime_str, sizeof(mtime_str), "%lu", + imp->imp_sec->ps_sepol_mtime); + mode_str[0] = imp->imp_sec->ps_sepol[0]; + } + spin_unlock(&imp->imp_sec->ps_lock); + ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); + rc = ret>>8; + } + + return rc; +} +#endif + +static inline int sptlrpc_sepol_needs_check(struct ptlrpc_sec *imp_sec) +{ + ktime_t checknext; + + if (send_sepol == 0) + return 0; + + if (send_sepol == -1) + /* send_sepol == -1 means fetch sepol status every time */ + return 1; + + spin_lock(&imp_sec->ps_lock); + checknext = imp_sec->ps_sepol_checknext; + spin_unlock(&imp_sec->ps_lock); + + /* next check is too far in time, please update */ + if (ktime_after(checknext, + ktime_add(ktime_get(), ktime_set(send_sepol, 0)))) + goto setnext; + + if (ktime_before(ktime_get(), checknext)) + /* too early to fetch sepol status */ + return 0; + +setnext: + /* define new sepol_checknext time */ + spin_lock(&imp_sec->ps_lock); + imp_sec->ps_sepol_checknext = ktime_add(ktime_get(), + ktime_set(send_sepol, 0)); + spin_unlock(&imp_sec->ps_lock); + + return 1; +} + +int sptlrpc_get_sepol(struct ptlrpc_request *req) +{ +#ifndef CONFIG_SECURITY_SELINUX + (req->rq_sepol)[0] = '\0'; + + if (unlikely(send_sepol != 0)) + CDEBUG(D_SEC, + "Client cannot report SELinux status, it was not built against libselinux.\n"); + return 0; +#else + struct ptlrpc_sec *imp_sec = req->rq_import->imp_sec; + int rc = 0; + + (req->rq_sepol)[0] = '\0'; + + if (send_sepol == 0) + return 0; + + if (!imp_sec) + return -EINVAL; + + /* Retrieve SELinux status info */ + if (sptlrpc_sepol_needs_check(imp_sec)) + rc = sepol_helper(req->rq_import); + if (likely(rc == 0)) { + spin_lock(&imp_sec->ps_lock); + memcpy(req->rq_sepol, imp_sec->ps_sepol, + sizeof(req->rq_sepol)); + spin_unlock(&imp_sec->ps_lock); + } + + return rc; +#endif +} +EXPORT_SYMBOL(sptlrpc_get_sepol); + /**************************************** * server side security * ****************************************/ diff --git a/fs/lustre/ptlrpc/sec_lproc.c b/fs/lustre/ptlrpc/sec_lproc.c index df7c667..04e421d 100644 --- a/fs/lustre/ptlrpc/sec_lproc.c +++ b/fs/lustre/ptlrpc/sec_lproc.c @@ -131,6 +131,78 @@ static int sptlrpc_ctxs_lprocfs_seq_show(struct seq_file *seq, void *v) LPROC_SEQ_FOPS_RO(sptlrpc_ctxs_lprocfs); +static ssize_t +lprocfs_wr_sptlrpc_sepol(struct file *file, const char __user *buffer, + size_t count, void *data) +{ + struct seq_file *seq = file->private_data; + struct obd_device *dev = seq->private; + struct client_obd *cli = &dev->u.cli; + struct obd_import *imp = cli->cl_import; + struct sepol_downcall_data *param; + int size = sizeof(*param); + int rc = 0; + + if (count < size) { + CERROR("%s: invalid data count = %lu, size = %d\n", + dev->obd_name, (unsigned long) count, size); + return -EINVAL; + } + + param = kzalloc(size, GFP_KERNEL); + if (!param) + return -ENOMEM; + + if (copy_from_user(param, buffer, size)) { + CERROR("%s: bad sepol data\n", dev->obd_name); + rc = -EFAULT; + goto out; + } + + if (param->sdd_magic != SEPOL_DOWNCALL_MAGIC) { + CERROR("%s: sepol downcall bad params\n", + dev->obd_name); + rc = -EINVAL; + goto out; + } + + if (param->sdd_sepol_len == 0 || + param->sdd_sepol_len >= sizeof(imp->imp_sec->ps_sepol)) { + CERROR("%s: invalid sepol data returned\n", + dev->obd_name); + rc = -EINVAL; + goto out; + } + rc = param->sdd_sepol_len; /* save sdd_sepol_len */ + kfree(param); + size = offsetof(struct sepol_downcall_data, + sdd_sepol[rc]); + + /* alloc again with real size */ + rc = 0; + param = kzalloc(size, GFP_KERNEL); + if (!param) + return -ENOMEM; + + if (copy_from_user(param, buffer, size)) { + CERROR("%s: bad sepol data\n", dev->obd_name); + rc = -EFAULT; + goto out; + } + + spin_lock(&imp->imp_sec->ps_lock); + snprintf(imp->imp_sec->ps_sepol, param->sdd_sepol_len + 1, "%s", + param->sdd_sepol); + imp->imp_sec->ps_sepol_mtime = param->sdd_sepol_mtime; + spin_unlock(&imp->imp_sec->ps_lock); + +out: + kfree(param); + + return rc ? rc : count; +} +LPROC_SEQ_FOPS_WR_ONLY(srpc, sptlrpc_sepol); + int sptlrpc_lprocfs_cliobd_attach(struct obd_device *dev) { if (strcmp(dev->obd_type->typ_name, LUSTRE_OSC_NAME) != 0 && @@ -145,6 +217,8 @@ int sptlrpc_lprocfs_cliobd_attach(struct obd_device *dev) &sptlrpc_info_lprocfs_fops); debugfs_create_file("srpc_contexts", 0444, dev->obd_debugfs_entry, dev, &sptlrpc_ctxs_lprocfs_fops); + debugfs_create_file("srpc_sepol", 0200, dev->obd_debugfs_entry, dev, + &srpc_sptlrpc_sepol_fops); return 0; } diff --git a/include/uapi/linux/lustre/lustre_idl.h b/include/uapi/linux/lustre/lustre_idl.h index f723d7b..77b9539 100644 --- a/include/uapi/linux/lustre/lustre_idl.h +++ b/include/uapi/linux/lustre/lustre_idl.h @@ -2936,6 +2936,19 @@ struct close_data { }; }; +/* sepol string format is: + * <1-digit for SELinux status>::: + */ +/* Max length of the sepol string + * Should be large enough to contain a sha512sum of the policy + */ +#define SELINUX_MODE_LEN 1 +#define SELINUX_POLICY_VER_LEN 3 /* 3 chars to leave room for the future */ +#define SELINUX_POLICY_HASH_LEN 64 +#define LUSTRE_NODEMAP_SEPOL_LENGTH (SELINUX_MODE_LEN + NAME_MAX + \ + SELINUX_POLICY_VER_LEN + \ + SELINUX_POLICY_HASH_LEN + 3) + /* * This is the lu_ladvise struct which goes out on the wire. * Corresponds to the userspace arg llapi_lu_ladvise. diff --git a/include/uapi/linux/lustre/lustre_user.h b/include/uapi/linux/lustre/lustre_user.h index 649aeeb..c1e9dca 100644 --- a/include/uapi/linux/lustre/lustre_user.h +++ b/include/uapi/linux/lustre/lustre_user.h @@ -798,6 +798,7 @@ static inline char *qtype_name(int qtype) } #define IDENTITY_DOWNCALL_MAGIC 0x6d6dd629 +#define SEPOL_DOWNCALL_MAGIC 0x8b8bb842 /* permission */ #define N_PERMS_MAX 64 @@ -819,6 +820,14 @@ struct identity_downcall_data { __u32 idd_groups[0]; }; +struct sepol_downcall_data { + __u32 sdd_magic; + time_t sdd_sepol_mtime; + __u16 sdd_sepol_len; + char sdd_sepol[0]; +}; + + /* lustre volatile file support * file name header: ".^L^S^T^R:volatile" */