Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
17 changes: 9 additions & 8 deletions disk-utils/mkswap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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
/*
Expand Down
64 changes: 64 additions & 0 deletions include/selinux-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,70 @@
#ifndef UTIL_LINUX_SELINUX_UTILS_H
#define UTIL_LINUX_SELINUX_UTILS_H

#ifdef HAVE_LIBSELINUX
#include <selinux/selinux.h>
#include <selinux/context.h>
#include <selinux/label.h>
#include <selinux/get_context_list.h>

/* 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);
Expand Down
151 changes: 136 additions & 15 deletions lib/selinux-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,128 @@
*
* Written by Karel Zak <kzak@redhat.com> [January 2021]
*/
#include <selinux/context.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
#include "selinux-utils.h"

#ifdef HAVE_LIBSELINUX
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <dlfcn.h>

#include "selinux-utils.h"
#ifdef HAVE_SYSTEMD_SD_DLOPEN_H
#include <systemd/sd-dlopen.h>
#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
*
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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 */
6 changes: 3 additions & 3 deletions libmount/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ lib_mount_sources = '''
'''.split() + [
list_h,
monotonic_c,
selinux_utils_c,
]

if LINUX
Expand Down Expand Up @@ -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',
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion libmount/mount.pc.in
Original file line number Diff line number Diff line change
Expand Up @@ -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@
Loading
Loading