From f1c8a02d75a83f0f2056437af3c5ab42ef2b1673 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Thu, 6 Dec 2018 10:40:45 +0100 Subject: [PATCH] LU-8955 nodemap: add SELinux policy info to nodemap 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>::: 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. Lustre-change: https://review.whamcloud.com/24420 Lustre-commit: 1f6cb3534e74f0c9462008c8088b5734b64ed41c Signed-off-by: Sebastien Buisson Change-Id: Ia1d178dd34f05ede020b490b1797a71dbae15d7b Reviewed-by: Patrick Farrell Reviewed-by: Li Dongyang Reviewed-by: Oleg Drokin Reviewed-on: https://review.whamcloud.com/34639 Tested-by: Jenkins Tested-by: Maloo --- lustre/include/lustre_nodemap.h | 4 + lustre/include/uapi/linux/lustre/lustre_idl.h | 13 ++++ lustre/mgs/mgs_handler.c | 1 + lustre/mgs/mgs_llog.c | 3 + lustre/ptlrpc/nodemap_handler.c | 107 ++++++++++++++++++++++++++ lustre/ptlrpc/nodemap_lproc.c | 72 +++++++++++++++++ lustre/utils/lctl.c | 3 + lustre/utils/obd.c | 64 +++++++++++++++ lustre/utils/obdctl.h | 1 + 9 files changed, 268 insertions(+) diff --git a/lustre/include/lustre_nodemap.h b/lustre/include/lustre_nodemap.h index 9fa9d1c..9d200bf 100644 --- a/lustre/include/lustre_nodemap.h +++ b/lustre/include/lustre_nodemap.h @@ -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); diff --git a/lustre/include/uapi/linux/lustre/lustre_idl.h b/lustre/include/uapi/linux/lustre/lustre_idl.h index 31bd606..f7d9a01 100644 --- a/lustre/include/uapi/linux/lustre/lustre_idl.h +++ b/lustre/include/uapi/linux/lustre/lustre_idl.h @@ -3579,6 +3579,19 @@ struct llog_update_record { */ }; +/* 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) + /* nodemap records, uses 32 byte record length */ #define LUSTRE_NODEMAP_NAME_LENGTH 16 struct nodemap_cluster_rec { diff --git a/lustre/mgs/mgs_handler.c b/lustre/mgs/mgs_handler.c index af08318..096c27e 100644 --- a/lustre/mgs/mgs_handler.c +++ b/lustre/mgs/mgs_handler.c @@ -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); diff --git a/lustre/mgs/mgs_llog.c b/lustre/mgs/mgs_llog.c index 24ecfc5..3c3cb5b 100644 --- a/lustre/mgs/mgs_llog.c +++ b/lustre/mgs/mgs_llog.c @@ -5454,6 +5454,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; } diff --git a/lustre/ptlrpc/nodemap_handler.c b/lustre/ptlrpc/nodemap_handler.c index 576779e..0443c3b 100644 --- a/lustre/ptlrpc/nodemap_handler.c +++ b/lustre/ptlrpc/nodemap_handler.c @@ -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); diff --git a/lustre/ptlrpc/nodemap_lproc.c b/lustre/ptlrpc/nodemap_lproc.c index 29ba26b..87a49a2 100644 --- a/lustre/ptlrpc/nodemap_lproc.c +++ b/lustre/ptlrpc/nodemap_lproc.c @@ -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, "" + * \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 @@ -1239,6 +1307,10 @@ static struct lprocfs_vars lprocfs_nodemap_vars[] = { .fops = &nodemap_fileset_fops, }, { + .name = "sepol", + .fops = &nodemap_sepol_fops, + }, + { .name = "exports", .fops = &nodemap_exports_fops, }, diff --git a/lustre/utils/lctl.c b/lustre/utils/lctl.c index 5e73d28..4ed25e8 100644 --- a/lustre/utils/lctl.c +++ b/lustre/utils/lctl.c @@ -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 "}, + {"nodemap_set_sepol", jt_nodemap_set_sepol, 0, + "set SELinux policy info on a nodemap\n" + "usage: nodemap_set_sepol "}, {"nodemap_test_nid", jt_nodemap_test_nid, 0, "usage: nodemap_test_nid "}, {"nodemap_test_id", jt_nodemap_test_id, 0, diff --git a/lustre/utils/obd.c b/lustre/utils/obd.c index 4fd72ac..d22a053 100644 --- a/lustre/utils/obd.c +++ b/lustre/utils/obd.c @@ -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 " + "--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 diff --git a/lustre/utils/obdctl.h b/lustre/utils/obdctl.h index a028c24..8ca38a5 100644 --- a/lustre/utils/obdctl.h +++ b/lustre/utils/obdctl.h @@ -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); -- 1.8.3.1