#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 *
***********************************************/
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 *
****************************************/
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 &&
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);
--- /dev/null
+/*
+ * 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;
+}