From e4e3808477be3f25acdf3ae6497198778b7859ac Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Tue, 23 Jun 2026 23:46:11 +0100 Subject: [PATCH] Switch libselinux to runtime optional via dlopen libselinux is built in most distros, but it is seldomly needed at runtime (e.g.: it is not used by default on Debian/Ubuntu). In order to allow building small images from the same packages, switch it to runtime optional by using dlopen(), and gracefully fallback if the library is not found. If libsystemd headers are available at build time stamp the ELF binaries using the ELF dlopen metadata format as defined by: https://uapi-group.org/specifications/specs/elf_dlopen_metadata/ so that packaging tools for rpm/deb can automatically derive dependencies for it. The implementation follows the same pattern already used for libcryptsetup for consistency. --- Makefile.am | 4 +- configure.ac | 9 ++- disk-utils/mkswap.c | 17 ++-- include/selinux-utils.h | 64 +++++++++++++++ lib/selinux-utils.c | 151 ++++++++++++++++++++++++++++++++---- libmount/meson.build | 6 +- libmount/mount.pc.in | 2 +- libmount/src/Makemodule.am | 9 ++- libmount/src/hook_selinux.c | 13 ++-- login-utils/Makemodule.am | 12 ++- login-utils/chfn.c | 2 +- login-utils/chsh.c | 2 +- login-utils/lslogins.c | 11 ++- login-utils/meson.build | 2 +- login-utils/sulogin.c | 31 ++++---- login-utils/vipw.c | 9 ++- meson.build | 58 +++++++++++--- misc-utils/Makemodule.am | 6 +- misc-utils/namei.c | 4 +- sys-utils/Makemodule.am | 12 ++- sys-utils/mount.c | 14 ++-- sys-utils/nsenter.c | 9 ++- 22 files changed, 357 insertions(+), 90 deletions(-) diff --git a/Makefile.am b/Makefile.am index 07ac717b729..d3f3ba0c523 100644 --- a/Makefile.am +++ b/Makefile.am @@ -212,9 +212,7 @@ edit_cmd = sed \ -e 's|@LIBBLKID_VERSION[@]|$(LIBBLKID_VERSION)|g' if HAVE_SELINUX -edit_cmd += -e 's|@LIBSELINUX[@]|libselinux|g' -else -edit_cmd += -e 's|@LIBSELINUX[@]||g' +edit_cmd += -e 's|@LIBDL[@]|-ldl|g' endif if HAVE_CRYPTSETUP diff --git a/configure.ac b/configure.ac index 89e05a45d68..998963d7a94 100644 --- a/configure.ac +++ b/configure.ac @@ -1075,13 +1075,20 @@ AS_IF([test "x$with_selinux" = xno], [ ) AS_IF([test "x$have_selinux" = xyes], [ AC_DEFINE([HAVE_LIBSELINUX], [1], [Define if SELinux is available]) - UL_PKG_STATIC([SELINUX_LIBS_STATIC], [libselinux]) AM_CONDITIONAL([HAVE_SELINUX], [true]) UL_SET_LIBS([$SELINUX_LIBS]) # This function is missing in old libselinux 1.xx versions AC_CHECK_FUNCS([security_get_initial_context]) UL_RESTORE_LIBS + + # Used to note dlopen dependency + # https://uapi-group.org/specifications/specs/elf_dlopen_metadata/ + AC_CHECK_HEADERS([systemd/sd-dlopen.h]) + + # libselinux is loaded with dlopen() + SELINUX_LIBS="-ldl" + SELINUX_LIBS_STATIC="-ldl" ]) ]) AC_SUBST([SELINUX_LIBS]) diff --git a/disk-utils/mkswap.c b/disk-utils/mkswap.c index d220d8f8ce6..5f82c437e3c 100644 --- a/disk-utils/mkswap.c +++ b/disk-utils/mkswap.c @@ -803,12 +803,13 @@ int main(int argc, char **argv) deinit_signature_page(&ctl); #ifdef HAVE_LIBSELINUX - if ((ctl.file || S_ISREG(ctl.devstat.st_mode)) && is_selinux_enabled() > 0) { + if ((ctl.file || S_ISREG(ctl.devstat.st_mode)) + && ul_load_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) { const char *context_string; char *oldcontext; context_t newcontext; - if (fgetfilecon(ctl.fd, &oldcontext) < 0) { + if (selinux_call(fgetfilecon)(ctl.fd, &oldcontext) < 0) { if (errno != ENODATA) err(EXIT_FAILURE, _("%s: unable to obtain selinux file label"), @@ -819,20 +820,20 @@ int main(int argc, char **argv) _("%s: unable to obtain default selinux file label"), ctl.devname); } - if (!(newcontext = context_new(oldcontext))) + if (!(newcontext = selinux_call(context_new)(oldcontext))) errx(EXIT_FAILURE, _("unable to create new selinux context")); - if (context_type_set(newcontext, SELINUX_SWAPFILE_TYPE)) + if (selinux_call(context_type_set)(newcontext, SELINUX_SWAPFILE_TYPE)) errx(EXIT_FAILURE, _("couldn't compute selinux context")); - context_string = context_str(newcontext); + context_string = selinux_call(context_str)(newcontext); if (strcmp(context_string, oldcontext)!=0) { - if (fsetfilecon(ctl.fd, context_string) && errno != ENOTSUP) + if (selinux_call(fsetfilecon)(ctl.fd, context_string) && errno != ENOTSUP) err(EXIT_FAILURE, _("unable to relabel %s to %s"), ctl.devname, context_string); } - context_free(newcontext); - freecon(oldcontext); + selinux_call(context_free)(newcontext); + selinux_call(freecon)(oldcontext); } #endif /* diff --git a/include/selinux-utils.h b/include/selinux-utils.h index 03795616efa..907e1a7b917 100644 --- a/include/selinux-utils.h +++ b/include/selinux-utils.h @@ -5,6 +5,70 @@ #ifndef UTIL_LINUX_SELINUX_UTILS_H #define UTIL_LINUX_SELINUX_UTILS_H +#ifdef HAVE_LIBSELINUX +#include +#include +#include +#include + +/* libselinux is an optional runtime dependency: it is loaded on demand with + * dlopen() rather than linked, so that the util-linux tools and libraries do + * not pull it in when SELinux is not actually used. The library functions are + * resolved with dlsym() into the 'ul_selinux' table below. + * + * Call ul_load_libselinux() before using any of the functions. It returns 0 + * when libselinux is available (and all the symbols have been resolved), or a + * negative value otherwise, so that callers can gracefully behave as if SELinux + * was disabled when the library is not available at runtime. The result is + * cached, so the library is opened at most once. + * + * The functions are called through the selinux_call() macro, for example: + * + * if (ul_load_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) + * selinux_call(getfilecon)(path, &con); + */ + +/* Pointers to libselinux functions (initialized by dlsym()) */ +struct ul_selinux_opers { + __typeof__(is_selinux_enabled) *is_selinux_enabled; + __typeof__(getfilecon) *getfilecon; + __typeof__(getfilecon_raw) *getfilecon_raw; + __typeof__(fgetfilecon) *fgetfilecon; + __typeof__(lgetfilecon) *lgetfilecon; + __typeof__(getcon) *getcon; + __typeof__(getprevcon) *getprevcon; + __typeof__(getpidcon) *getpidcon; + __typeof__(getseuserbyname) *getseuserbyname; + __typeof__(setfilecon) *setfilecon; + __typeof__(fsetfilecon) *fsetfilecon; + __typeof__(setfscreatecon) *setfscreatecon; + __typeof__(setexeccon) *setexeccon; + __typeof__(freecon) *freecon; + __typeof__(selinux_check_access) *selinux_check_access; + __typeof__(security_compute_relabel) *security_compute_relabel; + __typeof__(security_get_initial_context) *security_get_initial_context; + __typeof__(selinux_file_context_cmp) *selinux_file_context_cmp; + __typeof__(selinux_trans_to_raw_context) *selinux_trans_to_raw_context; + __typeof__(string_to_security_class) *string_to_security_class; + __typeof__(get_default_context_with_level) *get_default_context_with_level; + __typeof__(selabel_open) *selabel_open; + __typeof__(selabel_lookup) *selabel_lookup; + __typeof__(selabel_close) *selabel_close; + __typeof__(context_new) *context_new; + __typeof__(context_type_set) *context_type_set; + __typeof__(context_str) *context_str; + __typeof__(context_free) *context_free; +}; + +extern struct ul_selinux_opers ul_selinux; + +extern int ul_load_libselinux(void); + +/* libselinux call -- libselinux is always loaded with dlopen() */ +#define selinux_call(_func) (ul_selinux._func) + +#endif /* HAVE_LIBSELINUX */ + extern int ul_setfscreatecon_from_file(char *orig_file); extern int ul_selinux_has_access(const char *classstr, const char *perm, char **user_cxt); extern int ul_selinux_get_default_context(const char *path, int st_mode, char **cxt); diff --git a/lib/selinux-utils.c b/lib/selinux-utils.c index c0df8912046..80a4da1dff3 100644 --- a/lib/selinux-utils.c +++ b/lib/selinux-utils.c @@ -4,15 +4,128 @@ * * Written by Karel Zak [January 2021] */ -#include -#include -#include +#include "selinux-utils.h" + +#ifdef HAVE_LIBSELINUX #include #include #include #include +#include -#include "selinux-utils.h" +#ifdef HAVE_SYSTEMD_SD_DLOPEN_H +#include +#endif + +#include "c.h" + +/* The ELF .note.dlopen note advertises the optional libselinux dependency to + * package managers and other tooling, see SD_ELF_NOTE_DLOPEN(3) and + * https://uapi-group.org/specifications/specs/elf_dlopen_metadata/ + */ +#ifdef HAVE_SYSTEMD_SD_DLOPEN_H +SD_ELF_NOTE_DLOPEN("selinux", + "Support for SELinux", + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + "libselinux.so.1"); +#endif + +/* Pointers to libselinux functions (initialized by dlsym()) */ +struct ul_selinux_opers ul_selinux; + +static void *libselinux_dl; + +/* libselinux function names and offsets in 'struct ul_selinux_opers' */ +struct selinux_sym { + const char *name; + size_t offset; /* offset of the symbol in ul_selinux_opers */ +}; + +#define DEF_SELINUX_SYM(_name) \ + { \ + .name = # _name, \ + .offset = offsetof(struct ul_selinux_opers, _name), \ + } + +/* All required symbols */ +static const struct selinux_sym selinux_symbols[] = { + DEF_SELINUX_SYM( is_selinux_enabled ), + DEF_SELINUX_SYM( getfilecon ), + DEF_SELINUX_SYM( getfilecon_raw ), + DEF_SELINUX_SYM( fgetfilecon ), + DEF_SELINUX_SYM( lgetfilecon ), + DEF_SELINUX_SYM( getcon ), + DEF_SELINUX_SYM( getprevcon ), + DEF_SELINUX_SYM( getpidcon ), + DEF_SELINUX_SYM( getseuserbyname ), + DEF_SELINUX_SYM( setfilecon ), + DEF_SELINUX_SYM( fsetfilecon ), + DEF_SELINUX_SYM( setfscreatecon ), + DEF_SELINUX_SYM( setexeccon ), + DEF_SELINUX_SYM( freecon ), + DEF_SELINUX_SYM( selinux_check_access ), + DEF_SELINUX_SYM( security_compute_relabel ), + DEF_SELINUX_SYM( security_get_initial_context ), + DEF_SELINUX_SYM( selinux_file_context_cmp ), + DEF_SELINUX_SYM( selinux_trans_to_raw_context ), + DEF_SELINUX_SYM( string_to_security_class ), + DEF_SELINUX_SYM( get_default_context_with_level ), + DEF_SELINUX_SYM( selabel_open ), + DEF_SELINUX_SYM( selabel_lookup ), + DEF_SELINUX_SYM( selabel_close ), + DEF_SELINUX_SYM( context_new ), + DEF_SELINUX_SYM( context_type_set ), + DEF_SELINUX_SYM( context_str ), + DEF_SELINUX_SYM( context_free ), +}; + +/* + * dlopen() libselinux and resolve all the symbols listed in selinux_symbols[] + * into the global 'ul_selinux' table. The result is cached, so the library is + * opened at most once. Returns 0 on success and a negative value when the + * library (or any required symbol) is not available, so that callers can + * gracefully behave as if SELinux was disabled. + */ +int ul_load_libselinux(void) +{ + static int status; /* 0 = not tried yet, 1 = loaded, -1 = failed */ + size_t i; + int flags = RTLD_LAZY | RTLD_LOCAL; + + if (status) + return status > 0 ? 0 : -ENOSYS; + +#ifdef RTLD_NODELETE + /* the handle is cached for the whole process lifetime, never unload it */ + flags |= RTLD_NODELETE; +#endif + libselinux_dl = dlopen("libselinux.so.1", flags); + if (!libselinux_dl) { + status = -1; + return -ENOSYS; + } + + /* clear errors first, then load all the libselinux symbols */ + dlerror(); + + for (i = 0; i < ARRAY_SIZE(selinux_symbols); i++) { + const struct selinux_sym *def = &selinux_symbols[i]; + void **sym; + + sym = (void **) ((char *) (&ul_selinux) + def->offset); + *sym = dlsym(libselinux_dl, def->name); + + if (dlerror()) { + dlclose(libselinux_dl); + libselinux_dl = NULL; + status = -1; + return -ENOSYS; + } + } + + status = 1; + return 0; +} /* set the SELinux security context used for _creating_ a new file system object * @@ -21,16 +134,16 @@ */ int ul_setfscreatecon_from_file(char *orig_file) { - if (is_selinux_enabled() > 0) { + if (ul_load_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) { char *scontext = NULL; - if (getfilecon(orig_file, &scontext) < 0) + if (selinux_call(getfilecon)(orig_file, &scontext) < 0) return -1; - if (setfscreatecon(scontext) < 0) { - freecon(scontext); + if (selinux_call(setfscreatecon)(scontext) < 0) { + selinux_call(freecon)(scontext); return -1; } - freecon(scontext); + selinux_call(freecon)(scontext); } return 0; } @@ -47,14 +160,17 @@ int ul_selinux_has_access(const char *classstr, const char *perm, char **user_cx if (user_cxt) *user_cxt = NULL; - if (getprevcon(&user) != 0) + if (ul_load_libselinux() != 0) return 0; - rc = selinux_check_access(user, user, classstr, perm, NULL); + if (selinux_call(getprevcon)(&user) != 0) + return 0; + + rc = selinux_call(selinux_check_access)(user, user, classstr, perm, NULL); if (rc != 0 && user_cxt) *user_cxt = user; else - freecon(user); + selinux_call(freecon)(user); return rc == 0 ? 1 : 0; } @@ -72,14 +188,19 @@ int ul_selinux_get_default_context(const char *path, int st_mode, char **cxt) *cxt = NULL; - hnd = selabel_open(SELABEL_CTX_FILE, options, SELABEL_NOPT); + if (ul_load_libselinux() != 0) + return -ENOSYS; + + hnd = selinux_call(selabel_open)(SELABEL_CTX_FILE, options, SELABEL_NOPT); if (!hnd) return -errno; - if (selabel_lookup(hnd, cxt, path, st_mode) != 0) + if (selinux_call(selabel_lookup)(hnd, cxt, path, st_mode) != 0) rc = -errno ; - selabel_close(hnd); + selinux_call(selabel_close)(hnd); return rc; } + +#endif /* HAVE_LIBSELINUX */ diff --git a/libmount/meson.build b/libmount/meson.build index 233e0ac6525..8f3c2d0394c 100644 --- a/libmount/meson.build +++ b/libmount/meson.build @@ -41,6 +41,7 @@ lib_mount_sources = ''' '''.split() + [ list_h, monotonic_c, + selinux_utils_c, ] if LINUX @@ -83,7 +84,7 @@ lib__mount = static_library( lib_mount_sources, include_directories : [dir_include, dir_libmount], - dependencies : [blkid_dep]) + dependencies : [blkid_dep] + lib_selinux_compile_deps) lib_mount_static = static_library( 'mount_static', @@ -94,10 +95,9 @@ lib_mount_static = static_library( mount_static_dep = declare_dependency(link_with: lib_mount_static, include_directories: '.') lib__mount_deps = [ - lib_selinux, cryptsetup_dlopen ? lib_dl : lib_cryptsetup, realtime_libs -] +] + lib_selinux_link_deps if conf.get('USE_LIBMOUNT_UDEV_SUPPORT').to_string() == '1' lib__mount_deps += [lib_systemd] diff --git a/libmount/mount.pc.in b/libmount/mount.pc.in index 37ff59bf2dd..f9f7bef38c7 100644 --- a/libmount/mount.pc.in +++ b/libmount/mount.pc.in @@ -17,7 +17,7 @@ includedir=@includedir@ Name: mount Description: mount library Version: @LIBMOUNT_VERSION@ -Requires.private: blkid @LIBSELINUX@ @LIBCRYPTSETUP@ +Requires.private: blkid @LIBCRYPTSETUP@ Cflags: -I${includedir}/libmount Libs: -L${libdir} -lmount Libs.private: @LIBDL@ diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index a017543b74e..5e1f84dcb41 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -7,6 +7,8 @@ usrlib_exec_LTLIBRARIES += libmount.la libmount_la_SOURCES = \ include/list.h \ lib/monotonic.c \ + lib/selinux-utils.c \ + include/selinux-utils.h \ \ libmount/src/mountP.h \ libmount/src/cache.c \ @@ -58,9 +60,13 @@ endif # LINUX libmount_la_LIBADD = \ libcommon.la \ libblkid.la \ - $(SELINUX_LIBS) \ $(REALTIME_LIBS) +if HAVE_SELINUX +# libselinux is loaded with dlopen() at runtime instead of being linked +libmount_la_LIBADD += $(SELINUX_LIBS) +endif + if HAVE_CRYPTSETUP if CRYPTSETUP_VIA_DLOPEN libmount_la_LIBADD += -ldl @@ -77,6 +83,7 @@ libmount_la_CFLAGS = \ $(AM_CFLAGS) \ $(SOLIB_CFLAGS) \ $(CRYPTSETUP_CFLAGS) \ + $(SELINUX_CFLAGS) \ -I$(ul_libblkid_incdir) \ -I$(ul_libmount_incdir) \ -I$(top_srcdir)/libmount/src diff --git a/libmount/src/hook_selinux.c b/libmount/src/hook_selinux.c index 0dfd473591e..9d35c8001bf 100644 --- a/libmount/src/hook_selinux.c +++ b/libmount/src/hook_selinux.c @@ -19,6 +19,7 @@ #include "mountP.h" #include "fileutils.h" #include "linux_version.h" +#include "selinux-utils.h" static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs) { @@ -80,7 +81,7 @@ static int hook_selinux_target( return 0; - rc = getfilecon_raw(tgt, &raw); + rc = selinux_call(getfilecon_raw)(tgt, &raw); if (rc <= 0 || !raw) { rc = errno ? -errno : -EINVAL; DBG_OBJ(HOOK, hs, ul_debug(" SELinux fix @target failed [rc=%d]", rc)); @@ -91,7 +92,7 @@ static int hook_selinux_target( if (!rc) rc = mnt_opt_set_quoted_value(opt, raw); if (raw) - freecon(raw); + selinux_call(freecon)(raw); return rc != 0 ? -MNT_ERR_MOUNTOPT : 0; } @@ -110,8 +111,8 @@ static int hook_prepare_options( if (!ol) return -EINVAL; - if (!is_selinux_enabled()) - /* Always remove SELinux garbage if SELinux disabled */ + if (ul_load_libselinux() != 0 || !selinux_call(is_selinux_enabled)()) + /* Remove SELinux garbage if libselinux is unavailable or SELinux disabled */ se_rem = 1; else if (mnt_optlist_is_remount(ol)) /* @@ -167,7 +168,7 @@ static int hook_prepare_options( hook_selinux_target); continue; } else { - rc = selinux_trans_to_raw_context(val, &raw); + rc = selinux_call(selinux_trans_to_raw_context)(val, &raw); if (rc == -1 || !raw) rc = -EINVAL; } @@ -177,7 +178,7 @@ static int hook_prepare_options( rc = mnt_opt_set_quoted_value(opt, raw); } if (raw) - freecon(raw); + selinux_call(freecon)(raw); /* temporary for broken fsconfig() syscall */ cxt->has_selinux_opt = 1; diff --git a/login-utils/Makemodule.am b/login-utils/Makemodule.am index a82e4b70f20..3273e1a8ebf 100644 --- a/login-utils/Makemodule.am +++ b/login-utils/Makemodule.am @@ -43,6 +43,9 @@ if HAVE_LIBCRYPT sulogin_LDADD += -lcrypt endif if HAVE_SELINUX +sulogin_SOURCES += \ + lib/selinux-utils.c \ + include/selinux-utils.h sulogin_LDADD += $(SELINUX_LIBS) endif @@ -66,9 +69,6 @@ endif if HAVE_AUDIT login_LDADD += $(AUDIT_LIBS) endif -if HAVE_SELINUX -login_LDADD += $(SELINUX_LIBS) -endif endif # BUILD_LOGIN @@ -219,6 +219,9 @@ lslogins_SOURCES = \ lslogins_LDADD = $(LDADD) libcommon.la libcommon_logindefs.la libsmartcols.la lslogins_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) if HAVE_SELINUX +lslogins_SOURCES += \ + lib/selinux-utils.c \ + include/selinux-utils.h lslogins_LDADD += $(SELINUX_LIBS) endif if HAVE_SYSTEMD @@ -242,6 +245,9 @@ vipw_SOURCES = \ login-utils/setpwnam.h vipw_LDADD = $(LDADD) libcommon.la if HAVE_SELINUX +vipw_SOURCES += \ + lib/selinux-utils.c \ + include/selinux-utils.h vipw_LDADD += $(SELINUX_LIBS) endif install-exec-hook-vipw:: diff --git a/login-utils/chfn.c b/login-utils/chfn.c index efc58159e56..c5971e7e86a 100644 --- a/login-utils/chfn.c +++ b/login-utils/chfn.c @@ -462,7 +462,7 @@ int main(int argc, char **argv) #endif #ifdef HAVE_LIBSELINUX - if (is_selinux_enabled() > 0) { + if (ul_load_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) { char *user_cxt = NULL; if (uid == 0 && !ul_selinux_has_access("passwd", "chfn", &user_cxt)) diff --git a/login-utils/chsh.c b/login-utils/chsh.c index 7effd3006b5..dbc1a408856 100644 --- a/login-utils/chsh.c +++ b/login-utils/chsh.c @@ -219,7 +219,7 @@ int main(int argc, char **argv) #endif #ifdef HAVE_LIBSELINUX - if (is_selinux_enabled() > 0) { + if (ul_load_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) { char *user_cxt = NULL; if (uid == 0 && !ul_selinux_has_access("passwd", "chsh", &user_cxt)) diff --git a/login-utils/lslogins.c b/login-utils/lslogins.c index fa69529f48b..89d7d5c1a94 100644 --- a/login-utils/lslogins.c +++ b/login-utils/lslogins.c @@ -40,6 +40,7 @@ #include #ifdef HAVE_LIBSELINUX # include +# include "selinux-utils.h" #endif #ifdef HAVE_LIBSYSTEMD @@ -1022,7 +1023,7 @@ static struct lslogins_user *get_user_info(struct lslogins_control *ctl, const c break; case COL_SELINUX: #ifdef HAVE_LIBSELINUX - if (!ctl->selinux_enabled || getcon(&user->context) != 0) + if (!ctl->selinux_enabled || selinux_call(getcon)(&user->context) != 0) user->context = NULL; #endif break; @@ -1512,7 +1513,8 @@ static void free_user(void *f) free(u->shell); free(u->pwd_status); #ifdef HAVE_LIBSELINUX - freecon(u->context); + if (u->context) + selinux_call(freecon)(u->context); #endif free(u); } @@ -1813,7 +1815,10 @@ int main(int argc, char *argv[]) case 'Z': { #ifdef HAVE_LIBSELINUX - int sl = is_selinux_enabled(); + int sl = 0; + + if (ul_load_libselinux() == 0) + sl = selinux_call(is_selinux_enabled)(); if (sl < 0) warn(_("failed to request selinux state")); else diff --git a/login-utils/meson.build b/login-utils/meson.build index 251d8de4601..0ffc3fc58e3 100644 --- a/login-utils/meson.build +++ b/login-utils/meson.build @@ -36,7 +36,7 @@ endif if lib_selinux.found() chfn_chsh_sources += selinux_utils_c - chfn_chsh_deps += [lib_selinux] + chfn_chsh_deps += lib_selinux_dlopen_deps endif chfn_sources = files( diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c index 23f5164e6bc..3dd5b79e962 100644 --- a/login-utils/sulogin.c +++ b/login-utils/sulogin.c @@ -47,6 +47,7 @@ #ifdef HAVE_LIBSELINUX # include # include +# include "selinux-utils.h" #endif #ifdef __linux__ @@ -107,8 +108,12 @@ static int is_selinux_enabled_cached(void) { static int cache = -1; - if (cache == -1) - cache = is_selinux_enabled(); + if (cache == -1) { + if (ul_load_libselinux() == 0) + cache = selinux_call(is_selinux_enabled)(); + else + cache = 0; + } return cache; } @@ -127,12 +132,12 @@ static void compute_login_context(void) if (is_selinux_enabled_cached() == 0) goto cleanup; - if (getseuserbyname("root", &seuser, &level) == -1) { + if (selinux_call(getseuserbyname)("root", &seuser, &level) == -1) { warnx(_("failed to compute seuser")); goto cleanup; } - if (get_default_context_with_level(seuser, level, NULL, &login_context) == -1) { + if (selinux_call(get_default_context_with_level)(seuser, level, NULL, &login_context) == -1) { warnx(_("failed to compute default context")); goto cleanup; } @@ -152,22 +157,22 @@ static void tcinit_selinux(struct console *con) if (!login_context) return; - if (fgetfilecon(con->fd, &con->reset_tty_context) == -1) { + if (selinux_call(fgetfilecon)(con->fd, &con->reset_tty_context) == -1) { warn(_("failed to get context of terminal %s"), con->tty); return; } - tclass = string_to_security_class("chr_file"); + tclass = selinux_call(string_to_security_class)("chr_file"); if (tclass == 0) { warnx(_("security class chr_file not available")); - freecon(con->reset_tty_context); + selinux_call(freecon)(con->reset_tty_context); con->reset_tty_context = NULL; return; } - if (security_compute_relabel(login_context, con->reset_tty_context, tclass, &con->user_tty_context) == -1) { + if (selinux_call(security_compute_relabel)(login_context, con->reset_tty_context, tclass, &con->user_tty_context) == -1) { warnx(_("failed to compute relabel context of terminal")); - freecon(con->reset_tty_context); + selinux_call(freecon)(con->reset_tty_context); con->reset_tty_context = NULL; return; } @@ -926,12 +931,12 @@ static void sushell(struct passwd *pwd, struct console *con) #ifdef HAVE_LIBSELINUX if (is_selinux_enabled_cached() == 1) { if (con->user_tty_context) { - if (fsetfilecon(con->fd, con->user_tty_context) == -1) + if (selinux_call(fsetfilecon)(con->fd, con->user_tty_context) == -1) warn(_("failed to set context to %s for terminal %s"), con->user_tty_context, con->tty); } if (login_context) { - if (setexeccon(login_context) == -1) + if (selinux_call(setexeccon)(login_context) == -1) warn(_("failed to set exec context to %s"), login_context); } } @@ -963,10 +968,10 @@ static void tcreset_selinux(struct list_head *consoles) continue; if (!con->reset_tty_context) continue; - if (fsetfilecon(con->fd, con->reset_tty_context) == -1) + if (selinux_call(fsetfilecon)(con->fd, con->reset_tty_context) == -1) warn(_("failed to reset context to %s for terminal %s"), con->reset_tty_context, con->tty); - freecon(con->reset_tty_context); + selinux_call(freecon)(con->reset_tty_context); con->reset_tty_context = NULL; } } diff --git a/login-utils/vipw.c b/login-utils/vipw.c index 25d14868501..c8b9c246ea7 100644 --- a/login-utils/vipw.c +++ b/login-utils/vipw.c @@ -71,6 +71,7 @@ #ifdef HAVE_LIBSELINUX # include +# include "selinux-utils.h" #endif #define FILENAMELEN 67 @@ -149,16 +150,16 @@ static void pw_write(void) warn(_("%s: create a link to %s failed"), orig_file, tmp); #ifdef HAVE_LIBSELINUX - if (is_selinux_enabled() > 0) { + if (ul_load_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) { char *passwd_context = NULL; int ret = 0; - if (getfilecon(orig_file, &passwd_context) < 0) { + if (selinux_call(getfilecon)(orig_file, &passwd_context) < 0) { warnx(_("Can't get context for %s"), orig_file); pw_error(orig_file, 1, 1); } - ret = setfilecon(tmp_file, passwd_context); - freecon(passwd_context); + ret = selinux_call(setfilecon)(tmp_file, passwd_context); + selinux_call(freecon)(passwd_context); if (ret != 0) { warnx(_("Can't set context for %s"), tmp_file); pw_error(tmp_file, 1, 1); diff --git a/meson.build b/meson.build index 0595aead9c5..c739b58f03d 100644 --- a/meson.build +++ b/meson.build @@ -493,12 +493,42 @@ lib_selinux = dependency( version : '>= 2.5', required : get_option('selinux')) conf.set('HAVE_LIBSELINUX', lib_selinux.found() ? 1 : false) + +lib_selinux_compile_deps = [] +lib_selinux_link_deps = [] + if lib_selinux.found() have = cc.has_function('security_get_initial_context', dependencies : lib_selinux) conf.set('HAVE_SECURITY_GET_INITIAL_CONTEXT', have ? 1 : false) + + # The ELF .note.dlopen note (used to advertise the optional dlopen() + # dependency) is emitted with the macro from when + # available. + conf.set('HAVE_SYSTEMD_SD_DLOPEN_H', + cc.has_header('systemd/sd-dlopen.h') ? 1 : false) + + if meson.version().version_compare('>= 0.62.0') + lib_dl = dependency('dl') + else + lib_dl = cc.find_library('dl') + endif + + lib_selinux_compile_deps = [ + lib_selinux.partial_dependency(compile_args : true, includes : true)] + lib_selinux_link_deps = [lib_dl] + + summary('selinux support (dlopen)', + 'enabled', + section : 'components') +else + summary('selinux support', + 'disabled', + section : 'components') endif +lib_selinux_dlopen_deps = lib_selinux_compile_deps + lib_selinux_link_deps + lib_magic = dependency( 'libmagic', required : get_option('magic')) @@ -1222,13 +1252,13 @@ opt = get_option('build-lslogins') \ exe = executable( 'lslogins', 'login-utils/lslogins.c', + selinux_utils_c, include_directories : includes, link_with : [lib_common, lib_smartcols, lib_common_logindefs] + (build_liblastlog2 ? [lib_lastlog2] : []), - dependencies : [lib_selinux, - lib_systemd], + dependencies : [lib_systemd] + lib_selinux_dlopen_deps, install_dir : usrbin_exec_dir, install : opt, build_by_default : opt) @@ -1245,9 +1275,10 @@ exe = executable( 'vipw', 'login-utils/vipw.c', 'login-utils/setpwnam.h', + selinux_utils_c, include_directories : includes, link_with : [lib_common], - dependencies : [lib_selinux], + dependencies : lib_selinux_dlopen_deps, install_dir : usrbin_exec_dir, install : opt, build_by_default : opt) @@ -1966,10 +1997,11 @@ opt = get_option('build-mount').allowed() exe = executable( 'mount', mount_sources, + selinux_utils_c, include_directories : includes, link_with : [lib_common, lib_smartcols], - dependencies : [lib_selinux, mount_dep], + dependencies : [mount_dep] + lib_selinux_dlopen_deps, install_mode : 'rwsr-xr-x', install : opt, build_by_default : opt) @@ -2207,9 +2239,10 @@ opt = get_option('build-nsenter') \ exe = executable( 'nsenter', nsenter_sources, + selinux_utils_c, include_directories : includes, link_with : [lib_common], - dependencies : [lib_selinux], + dependencies : lib_selinux_dlopen_deps, install_dir : usrbin_exec_dir, install : opt, build_by_default : opt) @@ -2223,9 +2256,10 @@ opt = opt and 'nsenter' in static_programs exe = executable( 'nsenter.static', nsenter_sources, + selinux_utils_c, include_directories : includes, link_with : [lib_common], - dependencies : [lib_selinux], + dependencies : lib_selinux_dlopen_deps, install_dir : usrbin_exec_dir, install : opt, build_by_default : opt) @@ -2392,7 +2426,7 @@ exe = executable( include_directories : includes, link_with : [lib_common, lib_uuid], - dependencies: [blkid_dep, lib_selinux], + dependencies: [blkid_dep] + lib_selinux_dlopen_deps, install_dir : sbindir, install : true) if not is_disabler(exe) @@ -2886,8 +2920,7 @@ exe = executable( include_directories : includes, link_with : [lib_common, lib_common_logindefs, lib_common_shells], dependencies : [lib_pam, - lib_audit, - lib_selinux], + lib_audit], install : opt, build_by_default : opt) if opt and not is_disabler(exe) @@ -2901,10 +2934,10 @@ opt = get_option('build-sulogin') \ exe = executable( 'sulogin', sulogin_sources, + selinux_utils_c, include_directories : includes, link_with : [lib_common], - dependencies : [lib_crypt, - lib_selinux], + dependencies : [lib_crypt] + lib_selinux_dlopen_deps, install_dir : sbindir, install : opt, build_by_default : opt) @@ -3005,8 +3038,9 @@ opt = get_option('build-namei').allowed() exe = executable( 'namei', namei_sources, + selinux_utils_c, include_directories : includes, - dependencies : [lib_selinux], + dependencies : lib_selinux_dlopen_deps, install_dir : usrbin_exec_dir, install : opt, build_by_default : opt) diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am index 4ff4a0090d9..8f14261192b 100644 --- a/misc-utils/Makemodule.am +++ b/misc-utils/Makemodule.am @@ -69,7 +69,11 @@ usrbin_exec_PROGRAMS += namei MANPAGES += misc-utils/namei.1 dist_noinst_DATA += misc-utils/namei.1.adoc namei_SOURCES = misc-utils/namei.c lib/strutils.c lib/idcache.c -namei_LDADD = $(LDADD) $(SELINUX_LIBS) +namei_LDADD = $(LDADD) +if HAVE_SELINUX +namei_SOURCES += lib/selinux-utils.c include/selinux-utils.h +namei_LDADD += $(SELINUX_LIBS) +endif endif if BUILD_WHEREIS diff --git a/misc-utils/namei.c b/misc-utils/namei.c index d3898e56436..81b244b6144 100644 --- a/misc-utils/namei.c +++ b/misc-utils/namei.c @@ -33,6 +33,7 @@ #ifdef HAVE_LIBSELINUX # include +# include "selinux-utils.h" #endif #include "c.h" @@ -165,7 +166,8 @@ new_namei(struct namei *parent, const char *path, const char *fname, int lev) #ifdef HAVE_LIBSELINUX /* Don't use is_selinux_enabled() here. We need info about a context * also on systems where SELinux is (temporary) disabled */ - nm->context_len = lgetfilecon(path, &nm->context); + if (ul_load_libselinux() == 0) + nm->context_len = selinux_call(lgetfilecon)(path, &nm->context); #endif if (lstat(path, &nm->st) != 0) { nm->noent = errno; diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index f98c811049a..bc3a4b38c42 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -371,9 +371,13 @@ dist_noinst_DATA += \ sys-utils/fstab.5.adoc \ sys-utils/umount.8.adoc mount_SOURCES = sys-utils/mount.c -mount_LDADD = $(LDADD) libcommon.la libmount.la $(SELINUX_LIBS) +mount_LDADD = $(LDADD) libcommon.la libmount.la mount_CFLAGS = $(SUID_CFLAGS) $(AM_CFLAGS) -I$(ul_libmount_incdir) mount_LDFLAGS = $(SUID_LDFLAGS) $(AM_LDFLAGS) +if HAVE_SELINUX +mount_SOURCES += lib/selinux-utils.c include/selinux-utils.h +mount_LDADD += $(SELINUX_LIBS) +endif umount_SOURCES = sys-utils/umount.c umount_LDADD = $(LDADD) libcommon.la libmount.la @@ -541,7 +545,11 @@ MANPAGES += sys-utils/nsenter.1 dist_noinst_DATA += sys-utils/nsenter.1.adoc nsenter_SOURCES = sys-utils/nsenter.c lib/exec_shell.c \ lib/caputils.c -nsenter_LDADD = $(LDADD) libcommon.la $(SELINUX_LIBS) +nsenter_LDADD = $(LDADD) libcommon.la +if HAVE_SELINUX +nsenter_SOURCES += lib/selinux-utils.c include/selinux-utils.h +nsenter_LDADD += $(SELINUX_LIBS) +endif if HAVE_STATIC_NSENTER usrbin_exec_PROGRAMS += nsenter.static diff --git a/sys-utils/mount.c b/sys-utils/mount.c index 377cafcf849..d90f0f66306 100644 --- a/sys-utils/mount.c +++ b/sys-utils/mount.c @@ -305,17 +305,19 @@ static void success_message(struct libmnt_context *cxt) #if defined(HAVE_LIBSELINUX) && defined(HAVE_SECURITY_GET_INITIAL_CONTEXT) # include # include +# include "selinux-utils.h" static void selinux_warning(struct libmnt_context *cxt, const char *tgt) { - if (tgt && mnt_context_is_verbose(cxt) && is_selinux_enabled() > 0) { + if (tgt && mnt_context_is_verbose(cxt) + && ul_load_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) { char *raw = NULL, *def = NULL; - if (getfilecon(tgt, &raw) > 0 - && security_get_initial_context("file", &def) == 0) { + if (selinux_call(getfilecon)(tgt, &raw) > 0 + && selinux_call(security_get_initial_context)("file", &def) == 0) { - if (!selinux_file_context_cmp(raw, def)) + if (!selinux_call(selinux_file_context_cmp)(raw, def)) printf(_( "mount: %s does not contain SELinux labels.\n" " You just mounted a file system that supports labels which does not\n" @@ -324,8 +326,8 @@ static void selinux_warning(struct libmnt_context *cxt, const char *tgt) " this file system. For more details see restorecon(8) and mount(8).\n"), tgt); } - freecon(raw); - freecon(def); + selinux_call(freecon)(raw); + selinux_call(freecon)(def); } } #else diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index c1706cef948..08d18047ebc 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -38,6 +38,7 @@ #ifdef HAVE_LIBSELINUX # include +# include "selinux-utils.h" #endif #ifndef HAVE_ENVIRON_DECL @@ -765,17 +766,17 @@ int main(int argc, char *argv[]) } #ifdef HAVE_LIBSELINUX - if (selinux && is_selinux_enabled() > 0) { + if (selinux && ul_load_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) { char *scon = NULL; if (!namespace_target_pid) errx(EXIT_FAILURE, _("no target PID specified for --follow-context")); - if (getpidcon(namespace_target_pid, &scon) < 0) + if (selinux_call(getpidcon)(namespace_target_pid, &scon) < 0) errx(EXIT_FAILURE, _("failed to get %d SELinux context"), (int) namespace_target_pid); - if (setexeccon(scon) < 0) + if (selinux_call(setexeccon)(scon) < 0) errx(EXIT_FAILURE, _("failed to set exec context to '%s'"), scon); - freecon(scon); + selinux_call(freecon)(scon); } #endif