From 1a75b4f92685147c4aaf915e9028d41cac305d69 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 22 Feb 2023 13:00:29 +0100 Subject: [PATCH] libmount: create a hook to set rootcontext=@target The final target (mountpoint) depends on others libmount functionality (X-mount.mkdir or --target-prefix). It means we cannot assume target when the library calls fix_opts() and translates selinux contexts. The solution is extra hook executed after mkdir. Signed-off-by: Karel Zak --- libmount/meson.build | 1 + libmount/src/Makemodule.am | 1 + libmount/src/context_mount.c | 15 ++--- libmount/src/hook_selinux_target.c | 92 ++++++++++++++++++++++++++++++ libmount/src/hooks.c | 3 + libmount/src/mountP.h | 3 + 6 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 libmount/src/hook_selinux_target.c diff --git a/libmount/meson.build b/libmount/meson.build index 4764fcd70f..49b9ea7bf9 100644 --- a/libmount/meson.build +++ b/libmount/meson.build @@ -47,6 +47,7 @@ if LINUX src/hook_mount.c src/hook_mount_legacy.c src/hook_mkdir.c + src/hook_selinux_target.c src/hook_loopdev.c src/hook_idmap.c src/context_umount.c diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index 437e47006d..d0c1d2236b 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -34,6 +34,7 @@ libmount_la_SOURCES += \ libmount/src/hook_mount.c \ libmount/src/hook_mount_legacy.c \ libmount/src/hook_mkdir.c \ + libmount/src/hook_selinux_target.c \ libmount/src/hook_subdir.c \ libmount/src/hook_owner.c \ libmount/src/hook_idmap.c \ diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c index eba64b6c24..16741c9fd4 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -164,13 +164,14 @@ static int fix_optstr(struct libmnt_context *cxt) const char *val = mnt_opt_get_value(opt); char *raw = NULL; - if (strcmp(opt_name, "rootcontext") == 0 && strcmp(val, "@target") == 0) { - rc = getfilecon_raw(cxt->fs->target, &raw); - if (rc <= 0 || !raw) - rc = errno ? -errno : -EINVAL; - else - rc = 0; /* getfilecon_raw(3) returns the size of the extended attribute value */ - } else { + /* @target placeholder is replaced in hook_selinux_target.c, + * because the mountpoint does not have to existe yet + * (for example "-o X-mount.mkdir=" or --target-prefix). + */ + if (strcmp(opt_name, "rootcontext") == 0 && + strcmp(val, "@target") == 0) + continue; + else { rc = selinux_trans_to_raw_context(val, &raw); if (rc == -1 || !raw) rc = -EINVAL; diff --git a/libmount/src/hook_selinux_target.c b/libmount/src/hook_selinux_target.c new file mode 100644 index 0000000000..d66e55a24c --- /dev/null +++ b/libmount/src/hook_selinux_target.c @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libmount from util-linux project. + * + * Copyright (C) 2023 Karel Zak + * + * libmount is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * + * Please, see the comment in libmount/src/hooks.c to understand how hooks work. + */ +#include "mountP.h" +#include "fileutils.h" + +#ifdef HAVE_LIBSELINUX +#include +#include + +static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs) +{ + void *data = NULL; + + DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name)); + + /* remove all our hooks and free hook data */ + while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) { + if (data) + free(data); + data = NULL; + } + + return 0; +} + +static int hook_prepare_target( + struct libmnt_context *cxt, + const struct libmnt_hookset *hs __attribute__((__unused__)), + void *data __attribute__((__unused__))) +{ + int rc = 0; + const char *tgt, *val; + char *raw = NULL; + struct libmnt_optlist *ol; + struct libmnt_opt *opt; + + assert(cxt); + + tgt = mnt_fs_get_target(cxt->fs); + if (!tgt) + return 0; + if (cxt->action != MNT_ACT_MOUNT) + return 0; + + ol = mnt_context_get_optlist(cxt); + if (!ol) + return -EINVAL; + + opt = mnt_optlist_get_named(ol, "rootcontext", NULL); + if (!opt) + return 0; + val = mnt_opt_get_value(opt); + if (!val || strcmp(val, "@target") != 0) + return 0; + + rc = getfilecon_raw(tgt, &raw); + if (rc <= 0 || !raw) + rc = errno ? -errno : -EINVAL; + else + rc = 0; /* getfilecon_raw(3) returns the size of the extended attribute value */ + + if (!rc) + rc = mnt_opt_set_quoted_value(opt, raw); + if (raw) + freecon(raw); + + return rc; +} + +const struct libmnt_hookset hookset_selinux_target = +{ + .name = "__selinux_target", + + .firststage = MNT_STAGE_PREP_TARGET, + .firstcall = hook_prepare_target, + + .deinit = hookset_deinit +}; + +#endif /* HAVE_LIBSELINUX */ diff --git a/libmount/src/hooks.c b/libmount/src/hooks.c index 539d440f65..2dd795590b 100644 --- a/libmount/src/hooks.c +++ b/libmount/src/hooks.c @@ -38,6 +38,9 @@ static const struct libmnt_hookset *hooksets[] = &hookset_veritydev, #endif &hookset_mkdir, +#ifdef HAVE_LIBSELINUX + &hookset_selinux_target, +#endif &hookset_subdir, #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT &hookset_mount, diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 80015f94c3..38c0a6d512 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -323,6 +323,9 @@ extern const struct libmnt_hookset hookset_loopdev; #ifdef HAVE_CRYPTSETUP extern const struct libmnt_hookset hookset_veritydev; #endif +#ifdef HAVE_LIBSELINUX +extern const struct libmnt_hookset hookset_selinux_target; +#endif extern int mnt_context_deinit_hooksets(struct libmnt_context *cxt); extern const struct libmnt_hookset *mnt_context_get_hookset(struct libmnt_context *cxt, const char *name); -- 2.47.2