Whamcloud - gitweb
LU-8955 nodemap: add SELinux policy info to nodemap 20/24420/27
authorSebastien Buisson <sbuisson@ddn.com>
Thu, 6 Dec 2018 09:40:45 +0000 (10:40 +0100)
committerOleg Drokin <green@whamcloud.com>
Wed, 30 Jan 2019 02:39:41 +0000 (02:39 +0000)
Give the ability to set SELinux policy information on a nodemap,
in a new nodemap field named 'sepol'.
When set, a client pertaining to this nodemap will be allowed to
connect only if the SELinux policy information it sends matches
the one stored in the nodemap.

Expected 'sepol' string format is:
<1-digit>:<policy name>:<policy version>:<policy hash>
1-digit is 0 for SELinux Permissive mode, 1 for Enforcing mode.

SELinux policy info of nodemap is stored permanently by using
'lctl set_param -P' commands. It means MDS and OSS will be able to
retrieve SELinux policy info of nodemap after a restart (when they
read params llog).
MGS will not see SElinux policy info after restart, but this does
not prevent the feature from working.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: Ia1d178dd34f05ede020b490b1797a71dbae15d7b
Reviewed-on: https://review.whamcloud.com/24420
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/include/lustre_nodemap.h
lustre/include/uapi/linux/lustre/lustre_idl.h
lustre/mgs/mgs_handler.c
lustre/mgs/mgs_llog.c
lustre/ptlrpc/nodemap_handler.c
lustre/ptlrpc/nodemap_lproc.c
lustre/utils/lctl.c
lustre/utils/obd.c
lustre/utils/obdctl.h

index 9fa9d1c..9d200bf 100644 (file)
@@ -106,6 +106,8 @@ struct lu_nodemap {
        struct nodemap_pde      *nm_pde_data;
        /* fileset the nodes of this nodemap are restricted to */
        char                     nm_fileset[PATH_MAX+1];
+       /* information about the expected SELinux policy on the nodes */
+       char                     nm_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1];
 
        /* used when loading/unloading nodemaps */
        struct list_head         nm_list;
@@ -144,6 +146,8 @@ int nodemap_del_idmap(const char *name, enum nodemap_id_type id_type,
                      const __u32 map[2]);
 int nodemap_set_fileset(const char *name, const char *fileset);
 char *nodemap_get_fileset(const struct lu_nodemap *nodemap);
+int nodemap_set_sepol(const char *name, const char *sepol);
+const char *nodemap_get_sepol(const struct lu_nodemap *nodemap);
 __u32 nodemap_map_id(struct lu_nodemap *nodemap,
                     enum nodemap_id_type id_type,
                     enum nodemap_tree_type tree_type, __u32 id);
index 118c98d..2284840 100644 (file)
@@ -3574,6 +3574,19 @@ struct llog_update_record {
        */
 };
 
+/* sepol string format is:
+ * <1-digit for SELinux status>:<policy name>:<policy version>:<policy hash>
+ */
+/* 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)
+
 /* nodemap records, uses 32 byte record length */
 #define LUSTRE_NODEMAP_NAME_LENGTH 16
 struct nodemap_cluster_rec {
index af08318..096c27e 100644 (file)
@@ -862,6 +862,7 @@ static int mgs_iocontrol_nodemap(const struct lu_env *env,
        case LCFG_NODEMAP_ADD_GIDMAP:
        case LCFG_NODEMAP_DEL_GIDMAP:
        case LCFG_NODEMAP_SET_FILESET:
+       case LCFG_NODEMAP_SET_SEPOL:
                if (lcfg->lcfg_bufcount != 3)
                        GOTO(out_lcfg, rc = -EINVAL);
                nodemap_name = lustre_cfg_string(lcfg, 1);
index 0908249..2740b2f 100644 (file)
@@ -5496,6 +5496,9 @@ int mgs_nodemap_cmd(const struct lu_env *env, struct mgs_device *mgs,
        case LCFG_NODEMAP_SET_FILESET:
                rc = nodemap_set_fileset(nodemap_name, param);
                break;
+       case LCFG_NODEMAP_SET_SEPOL:
+               rc = nodemap_set_sepol(nodemap_name, param);
+               break;
        default:
                rc = -EINVAL;
        }
index 576779e..0443c3b 100644 (file)
@@ -984,6 +984,111 @@ char *nodemap_get_fileset(const struct lu_nodemap *nodemap)
 }
 EXPORT_SYMBOL(nodemap_get_fileset);
 
+static int nodemap_validate_sepol(const char *sepol)
+{
+       char buf[LUSTRE_NODEMAP_SEPOL_LENGTH + 1];
+       char *p = (char *)sepol;
+       char *q = buf;
+       char polname[NAME_MAX + 1] = "";
+       char hash[SELINUX_POLICY_HASH_LEN + 1] = "";
+       unsigned char mode;
+       unsigned short ver;
+
+       CLASSERT(sizeof(buf) == sizeof(((struct lu_nodemap *)0)->nm_sepol));
+
+       if (sepol == NULL)
+               return -EINVAL;
+
+       /* we allow sepol = "" which means clear SELinux policy info */
+       if (sepol[0] == '\0')
+               return 0;
+
+       /* make a copy of sepol, by replacing ':' with space
+        * so that we can use sscanf over the string
+        */
+       while (p-sepol < sizeof(buf)) {
+               if (*p == ':')
+                       *q = ' ';
+               else
+                       *q = *p;
+               if (*p == '\0')
+                       break;
+               p++;
+               q++;
+       }
+       if (p-sepol == sizeof(buf))
+               return -ENAMETOOLONG;
+
+       if (sscanf(buf, "%1hhu %s %hu %s", &mode, polname, &ver, hash) != 4)
+               return -EINVAL;
+
+       if (mode != 0 && mode != 1)
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ * set SELinux policy on nodemap
+ * \param      name            nodemap to set SELinux policy info on
+ * \param      sepol           string containing SELinux policy info
+ * \retval     0 on success
+ *
+ * set SELinux policy info on the named nodemap
+ */
+int nodemap_set_sepol(const char *name, const char *sepol)
+{
+       struct lu_nodemap       *nodemap = NULL;
+       int                      rc;
+
+       rc = nodemap_validate_sepol(sepol);
+       if (rc < 0)
+               GOTO(out, rc);
+
+       mutex_lock(&active_config_lock);
+       nodemap = nodemap_lookup(name);
+       if (IS_ERR(nodemap)) {
+               mutex_unlock(&active_config_lock);
+               GOTO(out, rc = PTR_ERR(nodemap));
+       }
+
+       if (is_default_nodemap(nodemap)) {
+               /* We do not want nodes in the default nodemap to have
+                * SELinux restrictions. Sec admin should create dedicated
+                * nodemap entries for this.
+                */
+               GOTO(out_putref, rc = -EINVAL);
+       }
+
+       /* truncation cannot happen, as string length was checked in
+        * nodemap_validate_sepol()
+        */
+       strlcpy(nodemap->nm_sepol, sepol, sizeof(nodemap->nm_sepol));
+
+out_putref:
+       mutex_unlock(&active_config_lock);
+       nodemap_putref(nodemap);
+out:
+       return rc;
+}
+EXPORT_SYMBOL(nodemap_set_sepol);
+
+/**
+ * get SELinux policy info defined on nodemap
+ * \param      nodemap         nodemap to get SELinux policy info from
+ * \retval     SELinux policy info, or NULL if not defined or not activated
+ *
+ * get the SELinux policy info defined on the nodemap
+ */
+const char *nodemap_get_sepol(const struct lu_nodemap *nodemap)
+{
+       if (is_default_nodemap(nodemap))
+               return NULL;
+       else
+               return (char *)nodemap->nm_sepol;
+}
+EXPORT_SYMBOL(nodemap_get_sepol);
+
 /**
  * Nodemap constructor
  *
@@ -1072,6 +1177,7 @@ struct lu_nodemap *nodemap_create(const char *name,
                nodemap->nm_squash_uid = NODEMAP_NOBODY_UID;
                nodemap->nm_squash_gid = NODEMAP_NOBODY_GID;
                nodemap->nm_fileset[0] = '\0';
+               nodemap->nm_sepol[0] = '\0';
                if (!is_default)
                        CWARN("adding nodemap '%s' to config without"
                              " default nodemap\n", nodemap->nm_name);
@@ -1092,6 +1198,7 @@ struct lu_nodemap *nodemap_create(const char *name,
                nodemap->nm_squash_uid = default_nodemap->nm_squash_uid;
                nodemap->nm_squash_gid = default_nodemap->nm_squash_gid;
                nodemap->nm_fileset[0] = '\0';
+               nodemap->nm_sepol[0] = '\0';
        }
 
        RETURN(nodemap);
index d0858d1..f8d964d 100644 (file)
@@ -241,6 +241,74 @@ out:
 LPROC_SEQ_FOPS(nodemap_fileset);
 
 /**
+ * Reads and prints the SELinux policy info for the given nodemap.
+ *
+ * \param      m               seq file in proc fs
+ * \param      data            unused
+ * \retval     0               success
+ */
+static int nodemap_sepol_seq_show(struct seq_file *m, void *data)
+{
+       struct lu_nodemap *nodemap;
+       int rc = 0;
+
+       mutex_lock(&active_config_lock);
+       nodemap = nodemap_lookup(m->private);
+       mutex_unlock(&active_config_lock);
+       if (IS_ERR(nodemap)) {
+               rc = PTR_ERR(nodemap);
+               CERROR("cannot find nodemap '%s': rc = %d\n",
+                       (char *)m->private, rc);
+               return rc;
+       }
+
+       seq_printf(m, "%s\n", nodemap_get_sepol(nodemap));
+       nodemap_putref(nodemap);
+       return rc;
+}
+
+/**
+ * Set SELinux policy info on a nodemap.
+ *
+ * \param[in] file      proc file
+ * \param[in] buffer    string, "<sepol>"
+ * \param[in] count     \a buffer length
+ * \param[in] off       unused
+ * \retval              \a count on success
+ * \retval              negative number on error
+ */
+static ssize_t
+nodemap_sepol_seq_write(struct file *file,
+                       const char __user *buffer,
+                       size_t count, loff_t *off)
+{
+       struct seq_file *m = file->private_data;
+       char sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1];
+       int rc = 0;
+
+       CLASSERT(sizeof(sepol) == sizeof(((struct lu_nodemap *)0)->nm_sepol));
+
+       if (count > 0) {
+               if (count >= sizeof(sepol))
+                       GOTO(out, rc = -ENAMETOOLONG);
+
+               if (copy_from_user(sepol, buffer, count))
+                       GOTO(out, rc = -EFAULT);
+
+               sepol[count] = '\0';
+
+               rc = nodemap_set_sepol(m->private, sepol);
+       }
+
+out:
+       if (rc != 0)
+               return rc;
+
+       return count;
+}
+LPROC_SEQ_FOPS(nodemap_sepol);
+
+/**
  * Reads and prints the exports attached to the given nodemap.
  *
  * \param      m               seq file in proc fs, stores nodemap
@@ -650,6 +718,10 @@ static struct lprocfs_vars lprocfs_nodemap_vars[] = {
                .fops           = &nodemap_fileset_fops,
        },
        {
+               .name           = "sepol",
+               .fops           = &nodemap_sepol_fops,
+       },
+       {
                .name           = "exports",
                .fops           = &nodemap_exports_fops,
        },
index 5e73d28..4ed25e8 100644 (file)
@@ -330,6 +330,9 @@ command_t cmdlist[] = {
        {"nodemap_set_fileset", jt_nodemap_set_fileset, 0,
         "set a fileset on a nodemap\n"
         "usage: nodemap_set_fileset <fileset>"},
+       {"nodemap_set_sepol", jt_nodemap_set_sepol, 0,
+        "set SELinux policy info on a nodemap\n"
+        "usage: nodemap_set_sepol <SELinux policy info>"},
        {"nodemap_test_nid", jt_nodemap_test_nid, 0,
         "usage: nodemap_test_nid <nid>"},
        {"nodemap_test_id", jt_nodemap_test_id, 0,
index 4fd72ac..d22a053 100644 (file)
@@ -3680,6 +3680,70 @@ int jt_nodemap_set_fileset(int argc, char **argv)
 }
 
 /**
+ * set SELinux policy info on a nodemap
+ *
+ * \param      argc            number of args
+ * \param      argv[]          variable string arguments
+ *
+ * --name                      nodemap name
+ * --sepol                     SELinux policy info
+ *
+ * \retval                     0 on success
+ */
+int jt_nodemap_set_sepol(int argc, char **argv)
+{
+       char *nodemap_name = NULL;
+       char *sepol = NULL;
+       int   rc = 0;
+       int   c;
+
+       static struct option long_options[] = {
+               {
+                       .name           = "name",
+                       .has_arg        = required_argument,
+                       .val            = 'n',
+               },
+               {
+                       .name           = "sepol",
+                       .has_arg        = required_argument,
+                       .val            = 's',
+               },
+               {
+                       .name = NULL,
+               }
+       };
+
+       while ((c = getopt_long(argc, argv, "n:s:",
+                               long_options, NULL)) != -1) {
+               switch (c) {
+               case 'n':
+                       nodemap_name = optarg;
+                       break;
+               case 's':
+                       sepol = optarg;
+                       break;
+               }
+       }
+
+       if (nodemap_name == NULL || sepol == NULL) {
+               fprintf(stderr, "usage: nodemap_set_sepol --name <name> "
+                               "--sepol <sepol>\n");
+               return -1;
+       }
+
+       rc = nodemap_cmd(LCFG_NODEMAP_SET_SEPOL, NULL, 0, argv[0],
+                        nodemap_name, sepol, NULL);
+       if (rc != 0) {
+               errno = -rc;
+               fprintf(stderr, "error: %s: cannot set sepol '%s' on nodemap "
+                               "'%s': rc = %d\n",
+                       jt_cmdname(argv[0]), sepol, nodemap_name, rc);
+       }
+
+       return rc;
+}
+
+/**
  * modify a nodemap's behavior
  *
  * \param      argc            number of args
index a028c24..8ca38a5 100644 (file)
@@ -190,6 +190,7 @@ int jt_nodemap_add_idmap(int argc, char **argv);
 int jt_nodemap_del_idmap(int argc, char **argv);
 int jt_nodemap_test_id(int argc, char **argv);
 int jt_nodemap_set_fileset(int argc, char **argv);
+int jt_nodemap_set_sepol(int argc, char **argv);
 int jt_nodemap_info(int argc, char **argv);
 int jt_changelog_register(int argc, char **argv);
 int jt_changelog_deregister(int argc, char **argv);