diff --git a/src/core/executor.c b/src/core/executor.c index 86fbaef347..e07a689074 100644 --- a/src/core/executor.c +++ b/src/core/executor.c @@ -199,10 +199,6 @@ int main(int argc, char *argv[]) { log_set_prohibit_ipc(true); log_setup(); - r = mac_init(); - if (r < 0) - return log_error_errno(r, "Failed to initialize MAC layer: %m"); - r = fdset_new_fill(/* filter_cloexec= */ 0, &fdset); if (r < 0) return log_error_errno(r, "Failed to create fd set: %m"); @@ -217,6 +213,13 @@ int main(int argc, char *argv[]) { log_open(); } + /* Initialize lazily. SMACK is just a few operations, but the SELinux is very slow as it requires + * loading the entire database in memory, so we will do it lazily only if it is actually needed, to + * avoid wasting 2ms-10ms for each sd-executor that gets spawned. */ + r = mac_init_lazy(); + if (r < 0) + return log_error_errno(r, "Failed to initialize MAC layer: %m"); + r = fdset_remove(fdset, fileno(arg_serialization)); if (r < 0) return log_error_errno(r, "Failed to remove serialization fd from fd set: %m"); diff --git a/src/shared/label-util.c b/src/shared/label-util.c index 7262da2b1f..308fbff761 100644 --- a/src/shared/label-util.c +++ b/src/shared/label-util.c @@ -117,14 +117,25 @@ int btrfs_subvol_make_label(const char *path) { return mac_smack_fix(path, 0); } -int mac_init(void) { +static int init_internal(bool lazy) { int r; assert(!(mac_selinux_use() && mac_smack_use())); - r = mac_selinux_init(); + if (lazy) + r = mac_selinux_init_lazy(); + else + r = mac_selinux_init(); if (r < 0) return r; return mac_smack_init(); } + +int mac_init_lazy(void) { + return init_internal(/* lazy=*/ true); +} + +int mac_init(void) { + return init_internal(/* lazy=*/ false); +} diff --git a/src/shared/label-util.h b/src/shared/label-util.h index 2f8c539618..7fb98c7656 100644 --- a/src/shared/label-util.h +++ b/src/shared/label-util.h @@ -26,3 +26,4 @@ int mknod_label(const char *pathname, mode_t mode, dev_t dev); int btrfs_subvol_make_label(const char *path); int mac_init(void); +int mac_init_lazy(void); diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c index a38a56f434..fb7da12fb6 100644 --- a/src/shared/selinux-util.c +++ b/src/shared/selinux-util.c @@ -33,10 +33,16 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(context_t, context_free, NULL); #define _cleanup_context_free_ _cleanup_(context_freep) +typedef enum Initialized { + UNINITIALIZED, + INITIALIZED, + LAZY_INITIALIZED, +} Initialized; + static int mac_selinux_reload(int seqno); static int cached_use = -1; -static bool initialized = false; +static Initialized initialized = UNINITIALIZED; static int last_policyload = 0; static struct selabel_handle *label_hnd = NULL; static bool have_status_page = false; @@ -104,28 +110,36 @@ void mac_selinux_retest(void) { #if HAVE_SELINUX static int open_label_db(void) { struct selabel_handle *hnd; - usec_t before_timestamp, after_timestamp; - + /* Avoid maybe-uninitialized false positives */ + usec_t before_timestamp = USEC_INFINITY, after_timestamp = USEC_INFINITY; # if HAVE_GENERIC_MALLINFO - generic_mallinfo before_mallinfo = generic_mallinfo_get(); + generic_mallinfo before_mallinfo = {}; # endif - before_timestamp = now(CLOCK_MONOTONIC); + + if (DEBUG_LOGGING) { +# if HAVE_GENERIC_MALLINFO + before_mallinfo = generic_mallinfo_get(); +# endif + before_timestamp = now(CLOCK_MONOTONIC); + } hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); if (!hnd) return log_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m"); - after_timestamp = now(CLOCK_MONOTONIC); + if (DEBUG_LOGGING) { + after_timestamp = now(CLOCK_MONOTONIC); # if HAVE_GENERIC_MALLINFO - generic_mallinfo after_mallinfo = generic_mallinfo_get(); - size_t l = LESS_BY((size_t) after_mallinfo.uordblks, (size_t) before_mallinfo.uordblks); - log_debug("Successfully loaded SELinux database in %s, size on heap is %zuK.", - FORMAT_TIMESPAN(after_timestamp - before_timestamp, 0), - DIV_ROUND_UP(l, 1024)); + generic_mallinfo after_mallinfo = generic_mallinfo_get(); + size_t l = LESS_BY((size_t) after_mallinfo.uordblks, (size_t) before_mallinfo.uordblks); + log_debug("Successfully loaded SELinux database in %s, size on heap is %zuK.", + FORMAT_TIMESPAN(after_timestamp - before_timestamp, 0), + DIV_ROUND_UP(l, 1024)); # else - log_debug("Successfully loaded SELinux database in %s.", - FORMAT_TIMESPAN(after_timestamp - before_timestamp, 0)); + log_debug("Successfully loaded SELinux database in %s.", + FORMAT_TIMESPAN(after_timestamp - before_timestamp, 0)); # endif + } /* release memory after measurement */ if (label_hnd) @@ -136,7 +150,7 @@ static int open_label_db(void) { } #endif -int mac_selinux_init(void) { +static int selinux_init(bool force) { #if HAVE_SELINUX static const LabelOps label_ops = { .pre = mac_selinux_label_pre, @@ -144,7 +158,12 @@ int mac_selinux_init(void) { }; int r; - if (initialized) + if (initialized == INITIALIZED) + return 1; + + /* Internal call from this module? Unless we were explicitly configured to allow lazy initialization + * bail out immediately. */ + if (!force && initialized != LAZY_INITIALIZED) return 0; if (!mac_selinux_use()) @@ -174,8 +193,23 @@ int mac_selinux_init(void) { * first call without any actual change. */ last_policyload = selinux_status_policyload(); - initialized = true; + initialized = INITIALIZED; + return 1; +#else + return 0; #endif +} + +int mac_selinux_init(void) { + return selinux_init(/* force= */ true); +} + +int mac_selinux_init_lazy(void) { +#if HAVE_SELINUX + if (initialized == UNINITIALIZED) + initialized = LAZY_INITIALIZED; /* We'll be back later */ +#endif + return 0; } @@ -299,7 +333,10 @@ int mac_selinux_fix_full( _cleanup_free_ char *p = NULL; int inode_fd, r; - /* if mac_selinux_init() wasn't called before we are a NOOP */ + r = selinux_init(/* force= */ false); + if (r <= 0) + return r; + if (!label_hnd) return 0; @@ -339,8 +376,11 @@ int mac_selinux_apply(const char *path, const char *label) { assert(path); #if HAVE_SELINUX - if (!mac_selinux_use()) - return 0; + int r; + + r = selinux_init(/* force= */ false); + if (r <= 0) + return r; assert(label); @@ -355,8 +395,11 @@ int mac_selinux_apply_fd(int fd, const char *path, const char *label) { assert(fd >= 0); #if HAVE_SELINUX - if (!mac_selinux_use()) - return 0; + int r; + + r = selinux_init(/* force= */ false); + if (r <= 0) + return r; assert(label); @@ -370,11 +413,15 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { #if HAVE_SELINUX _cleanup_freecon_ char *mycon = NULL, *fcon = NULL; security_class_t sclass; + int r; assert(exe); assert(label); - if (!mac_selinux_use()) + r = selinux_init(/* force= */ false); + if (r < 0) + return r; + if (r == 0) return -EOPNOTSUPP; if (getcon_raw(&mycon) < 0) @@ -401,7 +448,12 @@ int mac_selinux_get_our_label(char **ret) { assert(ret); #if HAVE_SELINUX - if (!mac_selinux_use()) + int r; + + r = selinux_init(/* force= */ false); + if (r < 0) + return r; + if (r == 0) return -EOPNOTSUPP; _cleanup_freecon_ char *con = NULL; @@ -423,12 +475,16 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * _cleanup_context_free_ context_t pcon = NULL, bcon = NULL; const char *range = NULL, *bcon_str = NULL; security_class_t sclass; + int r; assert(socket_fd >= 0); assert(exe); assert(ret_label); - if (!mac_selinux_use()) + r = selinux_init(/* force= */ false); + if (r < 0) + return r; + if (r == 0) return -EOPNOTSUPP; if (getcon_raw(&mycon) < 0) @@ -496,6 +552,10 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) assert(abspath); assert(path_is_absolute(abspath)); + r = selinux_init(/* force= */ false); + if (r <= 0) + return r; + /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ mac_selinux_maybe_reload(); if (!label_hnd) @@ -526,8 +586,11 @@ int mac_selinux_create_file_prepare_at( _cleanup_free_ char *abspath = NULL; int r; - if (dir_fd < 0 && dir_fd != AT_FDCWD) - return -EBADF; + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + + r = selinux_init(/* force= */ false); + if (r <= 0) + return r; if (!label_hnd) return 0; @@ -554,12 +617,14 @@ int mac_selinux_create_file_prepare_at( int mac_selinux_create_file_prepare_label(const char *path, const char *label) { #if HAVE_SELINUX + int r; if (!label) return 0; - if (!mac_selinux_use()) - return 0; + r = selinux_init(/* force= */ false); + if (r <= 0) + return r; if (setfscreatecon_raw(label) < 0) return log_enforcing_errno(errno, "Failed to set specified SELinux security context '%s' for '%s': %m", label, strna(path)); @@ -572,7 +637,7 @@ void mac_selinux_create_file_clear(void) { #if HAVE_SELINUX PROTECT_ERRNO; - if (!mac_selinux_use()) + if (selinux_init(/* force= */ false) <= 0) return; setfscreatecon_raw(NULL); @@ -582,10 +647,13 @@ void mac_selinux_create_file_clear(void) { int mac_selinux_create_socket_prepare(const char *label) { #if HAVE_SELINUX + int r; + assert(label); - if (!mac_selinux_use()) - return 0; + r = selinux_init(/* force= */ false); + if (r <= 0) + return r; if (setsockcreatecon(label) < 0) return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label); @@ -599,7 +667,7 @@ void mac_selinux_create_socket_clear(void) { #if HAVE_SELINUX PROTECT_ERRNO; - if (!mac_selinux_use()) + if (selinux_init(/* force= */ false) <= 0) return; setsockcreatecon_raw(NULL); @@ -622,6 +690,9 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { assert(addr); assert(addrlen >= sizeof(sa_family_t)); + if (selinux_init(/* force= */ false) <= 0) + goto skipped; + if (!label_hnd) goto skipped; diff --git a/src/shared/selinux-util.h b/src/shared/selinux-util.h index 238550ef52..97ab5ebb2f 100644 --- a/src/shared/selinux-util.h +++ b/src/shared/selinux-util.h @@ -21,6 +21,7 @@ void mac_selinux_retest(void); bool mac_selinux_enforcing(void); int mac_selinux_init(void); +int mac_selinux_init_lazy(void); void mac_selinux_maybe_reload(void); void mac_selinux_finish(void);