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