From c61168239eff571aefc2a695ef12ae355230e611 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Fri, 16 Jun 2017 14:36:23 +0200 Subject: [PATCH] LU-8955 sec: create new function sptlrpc_get_sepol() 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. Signed-off-by: Sebastien Buisson Change-Id: I7a4b955f36c5e0f4eca1561beae2b40fec88d570 Reviewed-on: https://review.whamcloud.com/24421 Tested-by: Jenkins Tested-by: Maloo Reviewed-by: Patrick Farrell Reviewed-by: Li Dongyang Reviewed-by: Oleg Drokin --- lustre/autoconf/lustre-core.m4 | 1 + lustre/include/lustre_net.h | 7 + lustre/include/lustre_sec.h | 12 + lustre/include/uapi/linux/lustre/lustre_user.h | 8 + lustre/ptlrpc/gss/sec_gss.c | 3 + lustre/ptlrpc/sec.c | 123 ++++++++ lustre/ptlrpc/sec_lproc.c | 101 ++++++- lustre/utils/.gitignore | 1 + lustre/utils/Makefile.am | 8 + lustre/utils/l_getsepol.c | 395 +++++++++++++++++++++++++ 10 files changed, 647 insertions(+), 12 deletions(-) create mode 100644 lustre/utils/l_getsepol.c diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 index 493e4e2..086d574 100644 --- a/lustre/autoconf/lustre-core.m4 +++ b/lustre/autoconf/lustre-core.m4 @@ -3521,6 +3521,7 @@ AM_CONDITIONAL(GSS_SSK, test x$enable_ssk = xyes) AM_CONDITIONAL(LIBPTHREAD, test x$enable_libpthread = xyes) AM_CONDITIONAL(HAVE_SYSTEMD, test "x$with_systemdsystemunitdir" != "xno") AM_CONDITIONAL(XATTR_HANDLER, test "x$lb_cv_compile_xattr_handler_flags" = xyes) +AM_CONDITIONAL(SELINUX, test "$SELINUX" = "-lselinux") ]) # LC_CONDITIONALS # diff --git a/lustre/include/lustre_net.h b/lustre/include/lustre_net.h index 8258421..7ad28ce 100644 --- a/lustre/include/lustre_net.h +++ b/lustre/include/lustre_net.h @@ -1048,6 +1048,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/lustre/include/lustre_sec.h b/lustre/include/lustre_sec.h index 96deb11..08f74aa 100644 --- a/lustre/include/lustre_sec.h +++ b/lustre/include/lustre_sec.h @@ -869,6 +869,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 @@ -1092,6 +1103,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/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index a747e45..0b143cb 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -949,6 +949,7 @@ static inline char *qtype_name(int qtype) } #define IDENTITY_DOWNCALL_MAGIC 0x6d6dd629 +#define SEPOL_DOWNCALL_MAGIC 0x8b8bb842 /* permission */ #define N_PERMS_MAX 64 @@ -970,6 +971,13 @@ 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]; +}; + #ifdef NEED_QUOTA_DEFS #ifndef QIF_BLIMITS #define QIF_BLIMITS 1 diff --git a/lustre/ptlrpc/gss/sec_gss.c b/lustre/ptlrpc/gss/sec_gss.c index e1a53a0..ab59c84 100644 --- a/lustre/ptlrpc/gss/sec_gss.c +++ b/lustre/ptlrpc/gss/sec_gss.c @@ -1102,6 +1102,9 @@ int gss_sec_create_common(struct gss_sec *gsec, sec->ps_import = class_import_get(imp); spin_lock_init(&sec->ps_lock); INIT_LIST_HEAD(&sec->ps_gc_list); + sec->ps_sepol_mtime = 0; + sec->ps_sepol_checknext = ktime_set(0, 0); + sec->ps_sepol[0] = '\0'; if (!svcctx) { sec->ps_gc_interval = GSS_GC_INTERVAL; diff --git a/lustre/ptlrpc/sec.c b/lustre/ptlrpc/sec.c index 5c44f89..666510a 100644 --- a/lustre/ptlrpc/sec.c +++ b/lustre/ptlrpc/sec.c @@ -54,6 +54,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 * ***********************************************/ @@ -1746,6 +1750,125 @@ int sptlrpc_svc_install_rvs_ctx(struct obd_import *imp, return policy->sp_sops->install_rctx(imp, ctx); } +/* 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 + }; + char *envp[] = { + [0] = "HOME=/", + [1] = "PATH=/sbin:/usr/sbin", + [2] = NULL + }; + signed short ret; + int rc = 0; + + if (imp == NULL || imp->imp_obd == NULL || + imp->imp_obd->obd_type == NULL) { + rc = -EINVAL; + } else { + argv[2] = 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; +} + +static inline int sptlrpc_sepol_needs_check(struct ptlrpc_sec *imp_sec) +{ + ktime_t checknext; + + if (send_sepol == 0 || !selinux_is_enabled()) + 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) +{ + struct ptlrpc_sec *imp_sec = req->rq_import->imp_sec; + int rc = 0; + + ENTRY; + + (req->rq_sepol)[0] = '\0'; + +#ifndef HAVE_SELINUX + if (unlikely(send_sepol != 0)) + CDEBUG(D_SEC, "Client cannot report SELinux status, " + "it was not built against libselinux.\n"); + RETURN(0); +#endif + + if (send_sepol == 0 || !selinux_is_enabled()) + RETURN(0); + + if (imp_sec == NULL) + 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); +} +EXPORT_SYMBOL(sptlrpc_get_sepol); + /**************************************** * server side security * ****************************************/ diff --git a/lustre/ptlrpc/sec_lproc.c b/lustre/ptlrpc/sec_lproc.c index 04ffd16..37bca07 100644 --- a/lustre/ptlrpc/sec_lproc.c +++ b/lustre/ptlrpc/sec_lproc.c @@ -140,9 +140,78 @@ out: LPROC_SEQ_FOPS_RO(sptlrpc_ctxs_lprocfs); +static ssize_t +lprocfs_sptlrpc_sepol_seq_write(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; + } + + OBD_ALLOC(param, size); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, buffer, size)) { + CERROR("%s: bad sepol data\n", dev->obd_name); + GOTO(out, rc = -EFAULT); + } + + if (param->sdd_magic != SEPOL_DOWNCALL_MAGIC) { + CERROR("%s: sepol downcall bad params\n", + dev->obd_name); + GOTO(out, rc = -EINVAL); + } + + 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); + GOTO(out, rc = -EINVAL); + } + rc = param->sdd_sepol_len; /* save sdd_sepol_len */ + OBD_FREE(param, size); + size = offsetof(struct sepol_downcall_data, + sdd_sepol[rc]); + + /* alloc again with real size */ + rc = 0; + OBD_ALLOC(param, size); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, buffer, size)) { + CERROR("%s: bad sepol data\n", dev->obd_name); + GOTO(out, rc = -EFAULT); + } + + 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: + if (param != NULL) + OBD_FREE(param, size); + + return rc ? rc : count; +} +LPROC_SEQ_FOPS_WR_ONLY(srpc, sptlrpc_sepol); + int sptlrpc_lprocfs_cliobd_attach(struct obd_device *dev) { - int rc; + int rc; if (strcmp(dev->obd_type->typ_name, LUSTRE_OSC_NAME) != 0 && strcmp(dev->obd_type->typ_name, LUSTRE_MDC_NAME) != 0 && @@ -156,21 +225,29 @@ int sptlrpc_lprocfs_cliobd_attach(struct obd_device *dev) rc = ldebugfs_seq_create(dev->obd_debugfs_entry, "srpc_info", 0444, &sptlrpc_info_lprocfs_fops, dev); - if (rc) { - CERROR("create proc entry srpc_info for %s: %d\n", - dev->obd_name, rc); - return rc; - } + if (rc) { + CERROR("create proc entry srpc_info for %s: %d\n", + dev->obd_name, rc); + return rc; + } rc = ldebugfs_seq_create(dev->obd_debugfs_entry, "srpc_contexts", 0444, &sptlrpc_ctxs_lprocfs_fops, dev); - if (rc) { - CERROR("create proc entry srpc_contexts for %s: %d\n", - dev->obd_name, rc); - return rc; - } + if (rc) { + CERROR("create proc entry srpc_contexts for %s: %d\n", + dev->obd_name, rc); + return rc; + } - return 0; + rc = ldebugfs_seq_create(dev->obd_debugfs_entry, "srpc_sepol", + 0200, &srpc_sptlrpc_sepol_fops, dev); + if (rc) { + CERROR("create proc entry srpc_sepol for %s: %d\n", + dev->obd_name, rc); + return rc; + } + + return 0; } EXPORT_SYMBOL(sptlrpc_lprocfs_cliobd_attach); diff --git a/lustre/utils/.gitignore b/lustre/utils/.gitignore index f3509ee..c98fff3 100644 --- a/lustre/utils/.gitignore +++ b/lustre/utils/.gitignore @@ -26,3 +26,4 @@ /lhsmd_posix /lhsmtool_posix /l_tunedisk +/l_getsepol diff --git a/lustre/utils/Makefile.am b/lustre/utils/Makefile.am index c7dd164..6ff6dc0 100644 --- a/lustre/utils/Makefile.am +++ b/lustre/utils/Makefile.am @@ -42,6 +42,10 @@ if LIBPTHREAD sbin_PROGRAMS += lhsmtool_posix endif +if SELINUX +sbin_PROGRAMS += l_getsepol +endif + lib_LIBRARIES = noinst_LIBRARIES = if LDISKFS_ENABLED @@ -210,6 +214,10 @@ lhsmtool_posix_SOURCES = lhsmtool_posix.c lhsmtool_posix_LDADD := liblustreapi.la $(PTHREAD_LIBS) lhsmtool_posix_DEPENDENCIES := liblustreapi.la +l_getsepol_SOURCES = l_getsepol.c +l_getsepol_LDADD := liblustreapi.la -lcrypto $(SELINUX) +l_getsepol_DEPENDENCIES := liblustreapi.la + wirecheck_SOURCES = wirecheck.c wirecheck_CPPFLAGS := -DCC="\"$(CC)\"" diff --git a/lustre/utils/l_getsepol.c b/lustre/utils/l_getsepol.c new file mode 100644 index 0000000..6f2d33c --- /dev/null +++ b/lustre/utils/l_getsepol.c @@ -0,0 +1,395 @@ +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see http://www.gnu.org/licenses + * + * GPL HEADER END + */ + +/* + * Copyright (c) 2016 DDN Storage + * Author: Sebastien Buisson sbuisson@ddn.com + */ + +/* + * lustre/utils/l_getsepol.c + * Userland helper to retrieve SELinux policy information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + + +static char *progname; +static char *obd_type = NULL, *obd_name = NULL; +static time_t ref_pol_mtime = 0; +static char ref_selinux_mode = -1; + +static void errlog(const char *fmt, ...) +{ + va_list args; + + openlog(progname, LOG_PERROR | LOG_PID, LOG_AUTHPRIV); + + va_start(args, fmt); + vsyslog(LOG_NOTICE, fmt, args); + va_end(args); + + closelog(); +} + +/* Retrieve name of policy loaded, and version */ +static int sepol_get_policy_info(char **policyname) +{ + char *pol_path; + + /* Name of loaded policy can be retrieved from policy root path */ + pol_path = strdup(selinux_policy_root()); + + if (!pol_path) { + *policyname = NULL; + errlog("can't get policy name: %s\n", strerror(errno)); + return -errno; + } + + *policyname = strdup(basename(pol_path)); + free(pol_path); + + return 0; +} + +/* Read binary SELinux policy, and compute hash */ +static int sepol_get_policy_data(const char *pol_bin_path, + unsigned char **mdval, unsigned int *mdsize) +{ + int fd; + char buffer[1024]; + ssize_t count = 1024; + EVP_MD_CTX *mdctx; + const EVP_MD *md = EVP_sha256(); /* use SHA-256 */ + int rc; + + /* Open policy file */ + fd = open(pol_bin_path, O_RDONLY); + if (fd < 0) { + errlog("can't open SELinux policy file %s: %s\n", pol_bin_path, + strerror(errno)); + rc = -ENOENT; + goto out; + } + + /* Read policy file */ + mdctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(mdctx, md, NULL); + while (count == 1024) { + count = read(fd, buffer, count); + if (count < 0) { + errlog("can't read SELinux policy file %s\n", + pol_bin_path); + rc = -errno; + close(fd); + goto out; + } + EVP_DigestUpdate(mdctx, buffer, count); + } + + /* Close policy file */ + rc = close(fd); + if (rc < 0) { + rc = -errno; + goto out; + } + + *mdsize = EVP_MD_size(md); + *mdval = malloc(*mdsize); + if (*mdval == NULL) { + rc = -ENOMEM; + goto out; + } + + EVP_DigestFinal_ex(mdctx, *mdval, NULL); + EVP_MD_CTX_destroy(mdctx); + +out: + return rc; +} + +int get_opts(int argc, char *const argv[]) +{ + static struct option long_opts[] = { + { .val = 'o', .name = "obd_type", + .has_arg = required_argument}, + { .val = 'n', .name = "obd_name", + .has_arg = required_argument}, + { .val = 't', .name = "sel_mtime", + .has_arg = required_argument}, + { .val = 'm', .name = "sel_mode", + .has_arg = required_argument}, + { .name = NULL } }; + char *short_opts = "o:n:t:m:"; + int opt; + int longidx; + char *sel_mtime = NULL, *sel_mode = NULL; + char *res; + + optind = 0; + while ((opt = getopt_long(argc, argv, short_opts, long_opts, + &longidx)) != EOF) { + switch (opt) { + case 'o': + obd_type = optarg; + break; + case 'n': + obd_name = optarg; + break; + case 't': + sel_mtime = optarg; + break; + case 'm': + sel_mode = optarg; + break; + default: + if (opt != '?') + fprintf(stderr, "Unknown option '%c'\n", opt); + return -EINVAL; + } + } + + if (optind != argc) { + errlog("incorrect arguments\n"); + return -EINVAL; + } + + if (!obd_type || !obd_name) + /* called without arg (presumably from command line): + * ignore everything */ + return 0; + + if (sel_mtime) { + ref_pol_mtime = (time_t)strtoul(sel_mtime, &res, 0); + if (*res != '\0') { + /* not a valid number */ + errlog("invalid sel_mtime"); + return -EINVAL; + } + } + + if (sel_mode) { + ref_selinux_mode = sel_mode[0] - '0'; + if (ref_selinux_mode != 0 && ref_selinux_mode != 1) { + /* not a valid enforcing mode */ + errlog("invalid sel_mode"); + return -EINVAL; + } + } + + return 0; +} + +/** + * Calculate SELinux status information. + * String that represents SELinux status info has the following format: + * ::: + * is a digit equal to 0 for SELinux Permissive mode, + * and 1 for Enforcing mode. + * When called from kernel space, it requires 4 args: + * - obd type + * - obd name + * - SELinux policy mtime + * - SELinux enforcing mode + * When called from command line (in this case without proper args), it prints + * SELinux status info to stdout. + */ +int main(int argc, char **argv) +{ + int policyver = 0; + char pol_bin_path[PATH_MAX + 1]; + struct stat st; + time_t policymtime; + int enforce; + struct sepol_downcall_data *data = NULL; + glob_t path; + int fd, size; + char *policy_type = NULL; + unsigned char *mdval = NULL; + unsigned int mdsize = 0; + char *p; + int idx; + int rc; + + progname = basename(argv[0]); + + rc = get_opts(argc, argv); + if (rc < 0) + goto out; + + /* Version of loaded policy */ + policyver = security_policyvers(); + if (policyver < 0) { + errlog("unknown policy version: %s\n", strerror(errno)); + rc = -errno; + goto out; + } + + /* Path of binary policy file */ + snprintf(pol_bin_path, sizeof(pol_bin_path), "%s.%d", + selinux_binary_policy_path(), policyver); + + /* Stat binary policy file */ + if (stat(pol_bin_path, &st)) { + errlog("can't stat %s: %s\n", pol_bin_path, strerror(errno)); + rc = -errno; + goto out; + } + policymtime = st.st_mtime; + + /* Determine if SELinux is in permissive or enforcing mode */ + enforce = security_getenforce(); + if (enforce < 0) { + errlog("can't getenforce: %s\n", strerror(errno)); + rc = -errno; + goto out; + } + + if (ref_pol_mtime == policymtime && ref_selinux_mode == enforce) { + /* Policy has not changed: return immediately */ + rc = 0; + goto out; + } + + /* Now we need to calculate SELinux status information */ + size = offsetof(struct sepol_downcall_data, + sdd_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1]); + data = malloc(size); + if (!data) { + errlog("malloc sepol downcall data(%d) failed!\n", size); + rc = -ENOMEM; + goto out; + } + memset(data, 0, size); + + /* Get policy name */ + rc = sepol_get_policy_info(&policy_type); + if (rc < 0) + goto out_data; + + /* Read binary SELinux policy, and compute hash */ + rc = sepol_get_policy_data(pol_bin_path, &mdval, &mdsize); + if (rc < 0) + goto out_poltyp; + + /* Put all info together and generate string + * to represent SELinux policy information + */ + rc = snprintf(data->sdd_sepol, LUSTRE_NODEMAP_SEPOL_LENGTH + 1, + "%.1d:%s:%u:", enforce, policy_type, policyver); + if (rc >= LUSTRE_NODEMAP_SEPOL_LENGTH + 1) { + rc = -EMSGSIZE; + goto out_mdval; + } + + p = data->sdd_sepol + strlen(data->sdd_sepol); + size = LUSTRE_NODEMAP_SEPOL_LENGTH + 1 - strlen(data->sdd_sepol); + for (idx = 0; idx < mdsize; idx++) { + rc = snprintf(p, size, "%02x", + (unsigned char)(mdval[idx])); + p += 2; + size -= 2; + if (size < 0 || rc >= size) { + rc = -EMSGSIZE; + goto out_mdval; + } + } + data->sdd_sepol_len = p - data->sdd_sepol; + + size = offsetof(struct sepol_downcall_data, + sdd_sepol[data->sdd_sepol_len]); + + if (!obd_type || !obd_name) { + /* called without arg (presumably from command line): + * print SELinux status and exit + */ + printf("SELinux status info: %.*s\n", + data->sdd_sepol_len, data->sdd_sepol); + return 0; + } + + data->sdd_magic = SEPOL_DOWNCALL_MAGIC; + data->sdd_sepol_mtime = policymtime; + /* Send SELinux policy info to kernelspace */ + rc = cfs_get_param_paths(&path, "%s/%s/srpc_sepol", obd_type, obd_name); + if (rc != 0) { + errlog("can't get param '%s/%s/srpc_sepol': %s\n", + obd_type, obd_name, strerror(errno)); + rc = -errno; + goto out_mdval; + } + + fd = open(path.gl_pathv[0], O_WRONLY); + if (fd < 0) { + errlog("can't open file '%s':%s\n", path.gl_pathv[0], + strerror(errno)); + rc = -errno; + goto out_params; + } + + rc = write(fd, data, size); + close(fd); + if (rc != size) { + errlog("partial write ret %d: %s\n", rc, strerror(errno)); + rc = -errno; + } else { + rc = 0; + } + +out_params: + cfs_free_param_data(&path); +out_mdval: + free(mdval); +out_poltyp: + free(policy_type); +out_data: + free(data); +out: + if (isatty(STDIN_FILENO)) + /* we are called from the command line */ + return rc < 0 ? -rc : rc; + else + return rc; +} -- 1.8.3.1