[PATCH v3] erofs-utils: support selinux file contexts
hsiangkao at aol.com
hsiangkao at aol.com
Mon Jun 8 22:34:14 AEST 2020
From: Gao Xiang <hsiangkao at redhat.com>
Add --file-contexts flag that allows passing a selinux
file_context file to setup file selabels.
Reviewed-and-tested-by: Li Guifu <bluce.lee at aliyun.com>
Signed-off-by: Gao Xiang <hsiangkao at redhat.com>
---
changes since v2:
- fix variable "secontext" memory leak in erofs_get_selabel_xattr.
configure.ac | 27 +++++++++
include/erofs/config.h | 21 +++++++
include/erofs/xattr.h | 3 +-
lib/config.c | 42 +++++++++++++
lib/exclude.c | 12 +---
lib/inode.c | 3 +-
lib/xattr.c | 132 ++++++++++++++++++++++++++++++++---------
man/mkfs.erofs.1 | 3 +
mkfs/Makefile.am | 4 +-
mkfs/main.c | 15 ++++-
10 files changed, 219 insertions(+), 43 deletions(-)
diff --git a/configure.ac b/configure.ac
index 870dfb9..5145971 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,15 @@ AC_ARG_WITH(uuid,
[AS_HELP_STRING([--without-uuid],
[Ignore presence of libuuid and disable uuid support @<:@default=enabled@:>@])])
+AC_ARG_WITH(selinux,
+ [AS_HELP_STRING([--with-selinux],
+ [enable and build with selinux support @<:@default=no@:>@])],
+ [case "$with_selinux" in
+ yes|no) ;;
+ *) AC_MSG_ERROR([invalid argument to --with-selinux])
+ ;;
+ esac], [with_selinux=no])
+
# Checks for libraries.
# Use customized LZ4 library path when specified.
AC_ARG_WITH(lz4-incdir,
@@ -160,6 +169,20 @@ return 0;
LIBS="${saved_LIBS}"
CPPFLAGS="${saved_CPPFLAGS}"], [have_uuid="no"])
+# Configure selinux
+AS_IF([test "x$with_selinux" != "xno"], [
+ PKG_CHECK_MODULES([libselinux], [libselinux])
+ # Paranoia: don't trust the result reported by pkgconfig before trying out
+ saved_LIBS="$LIBS"
+ saved_CPPFLAGS=${CPPFLAGS}
+ CPPFLAGS="${libselinux_CFLAGS} ${CPPFLAGS}"
+ LIBS="${libselinux_LIBS} $LIBS"
+ AC_CHECK_LIB(selinux, selabel_lookup, [
+ have_selinux="yes" ], [
+ AC_MSG_ERROR([libselinux doesn't work properly])])
+ LIBS="${saved_LIBS}"
+ CPPFLAGS="${saved_CPPFLAGS}"], [have_selinux="no"])
+
# Configure lz4
test -z $LZ4_LIBS && LZ4_LIBS='-llz4'
@@ -199,6 +222,10 @@ if test "x$have_uuid" = "xyes"; then
AC_DEFINE([HAVE_LIBUUID], 1, [Define to 1 if libuuid is found])
fi
+if test "x$have_selinux" = "xyes"; then
+ AC_DEFINE([HAVE_LIBSELINUX], 1, [Define to 1 if libselinux is found])
+fi
+
if test "x${have_lz4}" = "xyes"; then
AC_DEFINE([LZ4_ENABLED], [1], [Define to 1 if lz4 is enabled.])
diff --git a/include/erofs/config.h b/include/erofs/config.h
index 2be05ee..2f09749 100644
--- a/include/erofs/config.h
+++ b/include/erofs/config.h
@@ -10,6 +10,12 @@
#define __EROFS_CONFIG_H
#include "defs.h"
+#include "err.h"
+
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
enum {
FORCE_INODE_COMPACT = 1,
@@ -22,6 +28,9 @@ struct erofs_configure {
bool c_dry_run;
bool c_legacy_compress;
+#ifdef HAVE_LIBSELINUX
+ struct selabel_handle *sehnd;
+#endif
/* related arguments for mkfs.erofs */
char *c_img_path;
char *c_src_path;
@@ -39,5 +48,17 @@ void erofs_init_configure(void);
void erofs_show_config(void);
void erofs_exit_configure(void);
+void erofs_set_fs_root(const char *rootdir);
+const char *erofs_fspath(const char *fullpath);
+
+#ifdef HAVE_LIBSELINUX
+int erofs_selabel_open(const char *file_contexts);
+#else
+static inline int erofs_selabel_open(const char *file_contexts)
+{
+ return -EINVAL;
+}
+#endif
+
#endif
diff --git a/include/erofs/xattr.h b/include/erofs/xattr.h
index 3dff1ea..2e99669 100644
--- a/include/erofs/xattr.h
+++ b/include/erofs/xattr.h
@@ -42,7 +42,8 @@
#define XATTR_NAME_POSIX_ACL_DEFAULT "system.posix_acl_default"
#endif
-int erofs_prepare_xattr_ibody(const char *path, struct list_head *ixattrs);
+int erofs_prepare_xattr_ibody(const char *path, mode_t mode,
+ struct list_head *ixattrs);
char *erofs_export_xattr_ibody(struct list_head *ixattrs, unsigned int size);
int erofs_build_shared_xattrs_from_path(const char *path);
diff --git a/lib/config.c b/lib/config.c
index cbbecce..da0c260 100644
--- a/lib/config.c
+++ b/lib/config.c
@@ -36,6 +36,48 @@ void erofs_show_config(void)
void erofs_exit_configure(void)
{
+#ifdef HAVE_LIBSELINUX
+ if (cfg.sehnd)
+ selabel_close(cfg.sehnd);
+#endif
+}
+
+static unsigned int fullpath_prefix; /* root directory prefix length */
+
+void erofs_set_fs_root(const char *rootdir)
+{
+ fullpath_prefix = strlen(rootdir);
+}
+
+const char *erofs_fspath(const char *fullpath)
+{
+ const char *s = fullpath + fullpath_prefix;
+
+ while (*s == '/')
+ s++;
+ return s;
+}
+
+#ifdef HAVE_LIBSELINUX
+int erofs_selabel_open(const char *file_contexts)
+{
+ struct selinux_opt seopts[] = {
+ { .type = SELABEL_OPT_PATH, .value = file_contexts }
+ };
+
+ if (cfg.sehnd) {
+ erofs_info("ignore duplicated file contexts \"%s\"",
+ file_contexts);
+ return -EBUSY;
+ }
+ cfg.sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+ if (!cfg.sehnd) {
+ erofs_err("failed to open file contexts \"%s\"",
+ file_contexts);
+ return -EINVAL;
+ }
+ return 0;
}
+#endif
diff --git a/lib/exclude.c b/lib/exclude.c
index 47b467d..73b3720 100644
--- a/lib/exclude.c
+++ b/lib/exclude.c
@@ -17,13 +17,6 @@
static LIST_HEAD(exclude_head);
static LIST_HEAD(regex_exclude_head);
-static unsigned int rpathlen; /* root directory prefix length */
-
-void erofs_exclude_set_root(const char *rootdir)
-{
- rpathlen = strlen(rootdir);
-}
-
static void dump_regerror(int errcode, const char *s, const regex_t *preg)
{
char str[512];
@@ -120,10 +113,7 @@ struct erofs_exclude_rule *erofs_is_exclude_path(const char *dir,
s = buf;
}
- s += rpathlen;
- while (*s == '/')
- s++;
-
+ s = erofs_fspath(s);
list_for_each_entry(r, &exclude_head, list) {
if (!strcmp(r->pattern, s))
return r;
diff --git a/lib/inode.c b/lib/inode.c
index 7114023..dff5f2c 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -827,7 +827,8 @@ struct erofs_inode *erofs_mkfs_build_tree(struct erofs_inode *dir)
struct dirent *dp;
struct erofs_dentry *d;
- ret = erofs_prepare_xattr_ibody(dir->i_srcpath, &dir->i_xattrs);
+ ret = erofs_prepare_xattr_ibody(dir->i_srcpath,
+ dir->i_mode, &dir->i_xattrs);
if (ret < 0)
return ERR_PTR(ret);
dir->xattr_isize = ret;
diff --git a/lib/xattr.c b/lib/xattr.c
index 1564016..543ed71 100644
--- a/lib/xattr.c
+++ b/lib/xattr.c
@@ -186,6 +186,49 @@ static struct xattr_item *parse_one_xattr(const char *path, const char *key,
return get_xattritem(prefix, kvbuf, len);
}
+static struct xattr_item *erofs_get_selabel_xattr(const char *srcpath,
+ mode_t mode)
+{
+#ifdef HAVE_LIBSELINUX
+ if (cfg.sehnd) {
+ char *secontext;
+ int ret;
+ unsigned int len[2];
+ char *kvbuf, *fspath;
+
+ ret = asprintf(&fspath, "/%s", erofs_fspath(srcpath));
+ if (ret <= 0)
+ return ERR_PTR(-ENOMEM);
+
+ ret = selabel_lookup(cfg.sehnd, &secontext, fspath, mode);
+ free(fspath);
+
+ if (ret) {
+ ret = -errno;
+ if (ret != -ENOENT) {
+ erofs_err("failed to lookup selabel for %s: %s",
+ srcpath, erofs_strerror(ret));
+ return ERR_PTR(ret);
+ }
+ /* secontext = "u:object_r:unlabeled:s0"; */
+ return NULL;
+ }
+
+ len[0] = sizeof("selinux") - 1;
+ len[1] = strlen(secontext);
+ kvbuf = malloc(len[0] + len[1] + 1);
+ if (!kvbuf) {
+ free(secontext);
+ return ERR_PTR(-ENOMEM);
+ }
+ sprintf(kvbuf, "selinux%s", secontext);
+ free(secontext);
+ return get_xattritem(EROFS_XATTR_INDEX_SECURITY, kvbuf, len);
+ }
+#endif
+ return NULL;
+}
+
static int inode_xattr_add(struct list_head *hlist, struct xattr_item *item)
{
struct inode_xattr_node *node = malloc(sizeof(*node));
@@ -215,19 +258,48 @@ static int shared_xattr_add(struct xattr_item *item)
return ++shared_xattrs_count;
}
-static int read_xattrs_from_file(const char *path, struct list_head *ixattrs)
+static int erofs_xattr_add(struct list_head *ixattrs, struct xattr_item *item)
+{
+ if (ixattrs)
+ return inode_xattr_add(ixattrs, item);
+
+ if (item->count == cfg.c_inline_xattr_tolerance + 1) {
+ int ret = shared_xattr_add(item);
+
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static bool erofs_is_skipped_xattr(const char *key)
+{
+#ifdef HAVE_LIBSELINUX
+ /* if sehnd is valid, selabels will be overridden */
+ if (cfg.sehnd && !strcmp(key, XATTR_SECURITY_PREFIX "selinux"))
+ return true;
+#endif
+ return false;
+}
+
+static int read_xattrs_from_file(const char *path, mode_t mode,
+ struct list_head *ixattrs)
{
- int ret = 0;
- char *keylst, *key;
ssize_t kllen = llistxattr(path, NULL, 0);
+ int ret;
+ char *keylst, *key, *klend;
+ unsigned int keylen;
+ struct xattr_item *item;
if (kllen < 0 && errno != ENODATA) {
erofs_err("llistxattr to get the size of names for %s failed",
path);
return -errno;
}
+
+ ret = 0;
if (kllen <= 1)
- return 0;
+ goto out;
keylst = malloc(kllen);
if (!keylst)
@@ -246,36 +318,42 @@ static int read_xattrs_from_file(const char *path, struct list_head *ixattrs)
* attribute keys. Use the remaining buffer length to determine
* the end of the list.
*/
- key = keylst;
- while (kllen > 0) {
- unsigned int keylen = strlen(key);
- struct xattr_item *item = parse_one_xattr(path, key, keylen);
+ klend = keylst + kllen;
+ ret = 0;
+
+ for (key = keylst; key != klend; key += keylen + 1) {
+ keylen = strlen(key);
+ if (erofs_is_skipped_xattr(key))
+ continue;
+ item = parse_one_xattr(path, key, keylen);
if (IS_ERR(item)) {
ret = PTR_ERR(item);
goto err;
}
- if (ixattrs) {
- ret = inode_xattr_add(ixattrs, item);
- if (ret < 0)
- goto err;
- } else if (item->count == cfg.c_inline_xattr_tolerance + 1) {
- ret = shared_xattr_add(item);
- if (ret < 0)
- goto err;
- ret = 0;
- }
- kllen -= keylen + 1;
- key += keylen + 1;
+ ret = erofs_xattr_add(ixattrs, item);
+ if (ret < 0)
+ goto err;
}
-err:
free(keylst);
+
+out:
+ /* if some selabel is avilable, need to add right now */
+ item = erofs_get_selabel_xattr(path, mode);
+ if (IS_ERR(item))
+ return PTR_ERR(item);
+ if (item)
+ ret = erofs_xattr_add(ixattrs, item);
return ret;
+err:
+ free(keylst);
+ return ret;
}
-int erofs_prepare_xattr_ibody(const char *path, struct list_head *ixattrs)
+int erofs_prepare_xattr_ibody(const char *path, mode_t mode,
+ struct list_head *ixattrs)
{
int ret;
struct inode_xattr_node *node;
@@ -284,7 +362,7 @@ int erofs_prepare_xattr_ibody(const char *path, struct list_head *ixattrs)
if (cfg.c_inline_xattr_tolerance < 0)
return 0;
- ret = read_xattrs_from_file(path, ixattrs);
+ ret = read_xattrs_from_file(path, mode, ixattrs);
if (ret < 0)
return ret;
@@ -345,16 +423,16 @@ static int erofs_count_all_xattrs_from_path(const char *path)
goto fail;
}
- ret = read_xattrs_from_file(buf, NULL);
- if (ret)
- goto fail;
-
ret = lstat64(buf, &st);
if (ret) {
ret = -errno;
goto fail;
}
+ ret = read_xattrs_from_file(buf, st.st_mode, NULL);
+ if (ret)
+ goto fail;
+
if (!S_ISDIR(st.st_mode))
continue;
diff --git a/man/mkfs.erofs.1 b/man/mkfs.erofs.1
index d47207a..891c5a8 100644
--- a/man/mkfs.erofs.1
+++ b/man/mkfs.erofs.1
@@ -60,6 +60,9 @@ You may give multiple `--exclude-path' options.
Ignore files that match the given regular expression.
You may give multiple `--exclude-regex` options.
.TP
+.BI "\-\-file-contexts=" file
+Specify a \fIfile_contexts\fR file to setup / override selinux labels.
+.TP
.B \-\-help
Display this help and exit.
.SH AUTHOR
diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am
index 9ce06d6..97ba148 100644
--- a/mkfs/Makefile.am
+++ b/mkfs/Makefile.am
@@ -3,8 +3,8 @@
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = mkfs.erofs
-AM_CPPFLAGS = ${libuuid_CFLAGS}
+AM_CPPFLAGS = ${libuuid_CFLAGS} ${libselinux_CFLAGS}
mkfs_erofs_SOURCES = main.c
mkfs_erofs_CFLAGS = -Wall -Werror -I$(top_srcdir)/include
-mkfs_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libuuid_LIBS}
+mkfs_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libuuid_LIBS} ${libselinux_LIBS}
diff --git a/mkfs/main.c b/mkfs/main.c
index 940d4e8..94bf1e6 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -33,6 +33,9 @@ static struct option long_options[] = {
{"help", no_argument, 0, 1},
{"exclude-path", required_argument, NULL, 2},
{"exclude-regex", required_argument, NULL, 3},
+#ifdef HAVE_LIBSELINUX
+ {"file-contexts", required_argument, NULL, 4},
+#endif
{0, 0, 0, 0},
};
@@ -60,6 +63,9 @@ static void usage(void)
" -T# set a fixed UNIX timestamp # to all files\n"
" --exclude-path=X avoid including file X (X = exact literal path)\n"
" --exclude-regex=X avoid including files that match X (X = regular expression)\n"
+#ifdef HAVE_LIBSELINUX
+ " --file-contexts=X specify a file contexts file to setup selinux labels\n"
+#endif
" --help display this help and exit\n"
"\nAvailable compressors are: ", stderr);
print_available_compressors(stderr, ", ");
@@ -198,6 +204,13 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
return opt;
}
break;
+
+ case 4:
+ opt = erofs_selabel_open(optarg);
+ if (opt && opt != -EBUSY)
+ return opt;
+ break;
+
case 1:
usage();
exit(0);
@@ -392,7 +405,7 @@ int main(int argc, char **argv)
}
erofs_show_config();
- erofs_exclude_set_root(cfg.c_src_path);
+ erofs_set_fs_root(cfg.c_src_path);
sb_bh = erofs_buffer_init();
if (IS_ERR(sb_bh)) {
--
2.24.0
More information about the Linux-erofs
mailing list