Whamcloud - gitweb
LU-8955 sec: create new function sptlrpc_get_sepol() 21/24421/31
authorSebastien Buisson <sbuisson@ddn.com>
Fri, 16 Jun 2017 12:36:23 +0000 (14:36 +0200)
committerOleg Drokin <green@whamcloud.com>
Wed, 30 Jan 2019 02:39:55 +0000 (02:39 +0000)
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:
<mode>:<name>:<version>:<hash>
where:
- <mode> is a digit telling if SELinux is in Permissive mode (0)
  or Enforcing mode (1)
- <name> is the name of the SELinux policy
- <version> is the version of the SELinux policy
- <hash> is the computed hash of the binary representation of the
  policy, as exported in /etc/selinux/<name>/policy/policy.<version>

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 <sbuisson@ddn.com>
Change-Id: I7a4b955f36c5e0f4eca1561beae2b40fec88d570
Reviewed-on: https://review.whamcloud.com/24421
Tested-by: Jenkins
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Patrick Farrell <pfarrell@whamcloud.com>
Reviewed-by: Li Dongyang <dongyangli@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/autoconf/lustre-core.m4
lustre/include/lustre_net.h
lustre/include/lustre_sec.h
lustre/include/uapi/linux/lustre/lustre_user.h
lustre/ptlrpc/gss/sec_gss.c
lustre/ptlrpc/sec.c
lustre/ptlrpc/sec_lproc.c
lustre/utils/.gitignore
lustre/utils/Makefile.am
lustre/utils/l_getsepol.c [new file with mode: 0644]

index 493e4e2..086d574 100644 (file)
@@ -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
 
 #
index 8258421..7ad28ce 100644 (file)
@@ -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:
+        * <mode>:<policy name>:<policy version>:<policy hash>
+        */
+       char rq_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1];
+
        /* client/server security flags */
        unsigned int
                                  rq_ctx_init:1,      /* context initiation */
index 96deb11..08f74aa 100644 (file)
@@ -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:
+        * <mode>:<policy name>:<policy version>:<policy hash>
+        */
+       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
index a747e45..0b143cb 100644 (file)
@@ -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
index e1a53a0..ab59c84 100644 (file)
@@ -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;
index 5c44f89..666510a 100644 (file)
 
 #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                 *
  ****************************************/
index 04ffd16..37bca07 100644 (file)
@@ -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);
 
index f3509ee..c98fff3 100644 (file)
@@ -26,3 +26,4 @@
 /lhsmd_posix
 /lhsmtool_posix
 /l_tunedisk
+/l_getsepol
index c7dd164..6ff6dc0 100644 (file)
@@ -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 (file)
index 0000000..6f2d33c
--- /dev/null
@@ -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 <sys/types.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <getopt.h>
+
+#include <openssl/evp.h>
+
+#include <selinux/selinux.h>
+
+#include <libcfs/util/param.h>
+#include <linux/lustre/lustre_user.h>
+#include <linux/lustre/lustre_idl.h>
+
+
+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:
+ * <mode>:<policy name>:<policy version>:<policy hash>
+ * <mode> 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;
+}