From: Karel Zak Date: Wed, 22 Feb 2023 20:26:11 +0000 (+0100) Subject: libmount: move selinux stuff to hook module X-Git-Tag: v2.39-rc1~59 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d5c515dc727b40550f2556f7ac70431cb924b4b7;p=thirdparty%2Futil-linux.git libmount: move selinux stuff to hook module * convert selinux contexts to raw format during "prep-options" stage * convert rootcontext=@target to raw context when final mountpoint path is available (after mkdir). Signed-off-by: Karel Zak --- diff --git a/libmount/meson.build b/libmount/meson.build index 49b9ea7bf9..42b6b75e67 100644 --- a/libmount/meson.build +++ b/libmount/meson.build @@ -47,7 +47,7 @@ if LINUX src/hook_mount.c src/hook_mount_legacy.c src/hook_mkdir.c - src/hook_selinux_target.c + src/hook_selinux.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 d0c1d2236b..d474aea0c7 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -34,7 +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_selinux.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 16741c9fd4..614cf1901b 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -15,20 +15,13 @@ * @title: Mount context * @short_description: high-level API to mount operation. */ - -#ifdef HAVE_LIBSELINUX -#include -#include -#endif - #include #include -#include "linux_version.h" #include "mountP.h" #include "strutils.h" -#if defined(HAVE_LIBSELINUX) || defined(HAVE_SMACK) +#if defined(HAVE_SMACK) static int is_option(const char *name, const char *const *names) { const char *const *p; @@ -39,7 +32,7 @@ static int is_option(const char *name, const char *const *names) } return 0; } -#endif /* HAVE_LIBSELINUX || HAVE_SMACK */ +#endif /* HAVE_SMACK */ /* * this has to be called after mnt_context_evaluate_permissions() @@ -51,9 +44,7 @@ static int fix_optstr(struct libmnt_context *cxt) struct libmnt_ns *ns_old; const char *val; int rc = 0; -#ifdef HAVE_LIBSELINUX - int se_fix = 0, se_rem = 0; -#endif + assert(cxt); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); @@ -120,76 +111,6 @@ static int fix_optstr(struct libmnt_context *cxt) if (!mnt_context_switch_ns(cxt, ns_old)) return -MNT_ERR_NAMESPACE; -#ifdef HAVE_LIBSELINUX - if (!is_selinux_enabled()) - /* Always remove SELinux garbage if SELinux disabled */ - se_rem = 1; - else if (mnt_optlist_is_remount(ol)) - /* - * Linux kernel < 2.6.39 does not support remount operation - * with any selinux specific mount options. - * - * Kernel 2.6.39 commits: ff36fe2c845cab2102e4826c1ffa0a6ebf487c65 - * 026eb167ae77244458fa4b4b9fc171209c079ba7 - * fix this odd behavior, so we don't have to care about it in - * userspace. - */ - se_rem = get_linux_version() < KERNEL_VERSION(2, 6, 39); - else - /* For normal mount, contexts are translated */ - se_fix = 1; - - /* Fix SELinux contexts */ - if (se_rem || se_fix) { - static const char *const selinux_options[] = { - "context", - "fscontext", - "defcontext", - "rootcontext", - "seclabel", - NULL - }; - struct libmnt_iter itr; - - mnt_reset_iter(&itr, MNT_ITER_FORWARD); - - while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) { - const char *opt_name = mnt_opt_get_name(opt); - - if (!is_option(opt_name, selinux_options)) - continue; - if (se_rem) - rc = mnt_optlist_remove_opt(ol, opt); - else if (se_fix && mnt_opt_has_value(opt)) { - const char *val = mnt_opt_get_value(opt); - char *raw = NULL; - - /* @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; - } - if (!rc) - rc = mnt_opt_set_quoted_value(opt, raw); - if (raw) - freecon(raw); - - /* temporary for broken fsconfig() syscall */ - cxt->has_selinux_opt = 1; - } - if (rc) - goto done; - } - } -#endif - #ifdef HAVE_SMACK /* Fix Smack */ if (access("/sys/fs/smackfs", F_OK) != 0) { diff --git a/libmount/src/hook_selinux.c b/libmount/src/hook_selinux.c new file mode 100644 index 0000000000..d6bae95c94 --- /dev/null +++ b/libmount/src/hook_selinux.c @@ -0,0 +1,203 @@ +/* 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. + */ +#ifdef HAVE_LIBSELINUX +#include +#include + +#include "mountP.h" +#include "fileutils.h" +#include "linux_version.h" + +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 inline int is_option(const char *name, const char *const *names) +{ + const char *const *p; + + for (p = names; p && *p; p++) { + if (strcmp(name, *p) == 0) + return 1; + } + return 0; +} + +/* Converts rootcontext=@target to the real selinxu context + */ +static int hook_selinux_target( + struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + void *data __attribute__((__unused__))) +{ + struct libmnt_optlist *ol; + struct libmnt_opt *opt; + const char *tgt, *val; + char *raw = NULL; + int rc = 0; + + 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; + DBG(HOOK, ul_debugobj(hs, " SELinux fix @target failed [rc=%d]", rc)); + } else { + DBG(HOOK, ul_debugobj(hs, " SELinux fix @target to %s", raw)); + 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 != 0 ? -MNT_ERR_MOUNTOPT : 0; +} + +static int hook_prepare_options( + struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + void *data __attribute__((__unused__))) +{ + int rc = 0, se_fix = 0, se_rem = 0; + struct libmnt_optlist *ol; + + assert(cxt); + + ol = mnt_context_get_optlist(cxt); + if (!ol) + return -EINVAL; + + if (!is_selinux_enabled()) + /* Always remove SELinux garbage if SELinux disabled */ + se_rem = 1; + else if (mnt_optlist_is_remount(ol)) + /* + * Linux kernel < 2.6.39 does not support remount operation + * with any selinux specific mount options. + * + * Kernel 2.6.39 commits: ff36fe2c845cab2102e4826c1ffa0a6ebf487c65 + * 026eb167ae77244458fa4b4b9fc171209c079ba7 + * fix this odd behavior, so we don't have to care about it in + * userspace. + */ + se_rem = get_linux_version() < KERNEL_VERSION(2, 6, 39); + else + /* For normal mount, contexts are translated */ + se_fix = 1; + + DBG(HOOK, ul_debugobj(hs, " SELinux fix options")); + + /* Fix SELinux contexts */ + if (se_rem || se_fix) { + static const char *const selinux_options[] = { + "context", + "fscontext", + "defcontext", + "rootcontext", + "seclabel", + NULL + }; + struct libmnt_iter itr; + struct libmnt_opt *opt; + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) { + const char *opt_name = mnt_opt_get_name(opt); + + if (!is_option(opt_name, selinux_options)) + continue; + if (se_rem) + rc = mnt_optlist_remove_opt(ol, opt); + else if (se_fix && mnt_opt_has_value(opt)) { + const char *val = mnt_opt_get_value(opt); + char *raw = NULL; + + /* @target placeholder is replaced later when target + * is already avalable. The mountpoint does not have to exist + * yet (for example "-o X-mount.mkdir=" or --target-prefix). + */ + if (strcmp(opt_name, "rootcontext") == 0 && + strcmp(val, "@target") == 0) { + rc = mnt_context_insert_hook(cxt, "__mkdir", + hs, MNT_STAGE_PREP_TARGET, NULL, + hook_selinux_target); + continue; + } else { + rc = selinux_trans_to_raw_context(val, &raw); + if (rc == -1 || !raw) + rc = -EINVAL; + } + if (!rc) { + DBG(HOOK, ul_debugobj(hs, " %s: %s to %s", + opt_name, val, raw)); + rc = mnt_opt_set_quoted_value(opt, raw); + } + if (raw) + freecon(raw); + + /* temporary for broken fsconfig() syscall */ + cxt->has_selinux_opt = 1; + } + if (rc) + break; + } + } + + return rc != 0 ? -MNT_ERR_MOUNTOPT : 0; +} + +const struct libmnt_hookset hookset_selinux = +{ + .name = "__selinux", + + .firststage = MNT_STAGE_PREP_OPTIONS, + .firstcall = hook_prepare_options, + + .deinit = hookset_deinit +}; + +#endif /* HAVE_LIBSELINUX */ diff --git a/libmount/src/hook_selinux_target.c b/libmount/src/hook_selinux_target.c deleted file mode 100644 index d66e55a24c..0000000000 --- a/libmount/src/hook_selinux_target.c +++ /dev/null @@ -1,92 +0,0 @@ -/* 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 db204ec28e..c8d45d4791 100644 --- a/libmount/src/hooks.c +++ b/libmount/src/hooks.c @@ -39,7 +39,7 @@ static const struct libmnt_hookset *hooksets[] = #endif &hookset_mkdir, #ifdef HAVE_LIBSELINUX - &hookset_selinux_target, + &hookset_selinux, #endif &hookset_subdir, #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index e390f39b28..d522799c59 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -324,7 +324,7 @@ extern const struct libmnt_hookset hookset_loopdev; extern const struct libmnt_hookset hookset_veritydev; #endif #ifdef HAVE_LIBSELINUX -extern const struct libmnt_hookset hookset_selinux_target; +extern const struct libmnt_hookset hookset_selinux; #endif extern int mnt_context_deinit_hooksets(struct libmnt_context *cxt);