From d0b224f845ce001a28d8e8c77410732617ab5b65 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 25 Oct 2025 13:41:33 +0900 Subject: [PATCH] libcrypt-util: turn into dlopen() dependency Note, this drops logging only test case for crypt_preferred_method(), as that requires explicitly dlopen() the library. But, we should test that make_salt() and friends automatically dlopen() it. --- README | 2 +- meson.build | 2 + src/firstboot/meson.build | 1 - src/home/meson.build | 4 -- src/shared/libcrypt-util.c | 75 ++++++++++++++++++++++++++++++++--- src/shared/libcrypt-util.h | 4 ++ src/shared/meson.build | 2 +- src/test/meson.build | 1 - src/test/test-dlopen-so.c | 2 + src/test/test-libcrypt-util.c | 6 --- 10 files changed, 80 insertions(+), 19 deletions(-) diff --git a/README b/README index 0fd153bebbb..2d6d30dc78e 100644 --- a/README +++ b/README @@ -212,7 +212,7 @@ REQUIREMENTS: newer though. TL;DR: turn audit off, still. glibc >= 2.31 - libxcrypt >= 4.4.0 + libxcrypt >= 4.4.0 (optional) libmount >= 2.30 (from util-linux) (util-linux *must* be built without --enable-libmount-support-mtab) libseccomp >= 2.4.0 (optional) diff --git a/meson.build b/meson.build index 4f98dbd9da5..ff8cb279a8e 100644 --- a/meson.build +++ b/meson.build @@ -1039,11 +1039,13 @@ endif if get_option('libc') == 'musl' libcrypt = [] + libcrypt_cflags = [] have = get_option('libcrypt').allowed() else libcrypt = dependency('libcrypt', 'libxcrypt', required : get_option('libcrypt'), version : '>=4.4.0') + libcrypt_cflags = libcrypt.partial_dependency(includes: true, compile_args: true) have = libcrypt.found() endif conf.set10('HAVE_LIBCRYPT', have) diff --git a/src/firstboot/meson.build b/src/firstboot/meson.build index 28c1d2703a8..e83eded6ada 100644 --- a/src/firstboot/meson.build +++ b/src/firstboot/meson.build @@ -6,6 +6,5 @@ executables += [ 'public' : true, 'conditions' : ['ENABLE_FIRSTBOOT'], 'sources' : files('firstboot.c'), - 'dependencies' : libcrypt, }, ] diff --git a/src/home/meson.build b/src/home/meson.build index dbb374ce4a1..1efee1619ef 100644 --- a/src/home/meson.build +++ b/src/home/meson.build @@ -64,7 +64,6 @@ executables += [ 'sources' : systemd_homed_sources, 'extract' : systemd_homed_extract_sources, 'dependencies' : [ - libcrypt, libm, libopenssl, threads, @@ -80,7 +79,6 @@ executables += [ ], 'dependencies' : [ libblkid_cflags, - libcrypt, libfdisk, libopenssl, libp11kit_cflags, @@ -93,7 +91,6 @@ executables += [ 'sources' : homectl_sources, 'objects' : ['systemd-homed'], 'dependencies' : [ - libcrypt, libdl, libopenssl, libp11kit_cflags, @@ -112,7 +109,6 @@ modules += [ 'conditions' : ['HAVE_PAM'], 'sources' : pam_systemd_home_sources, 'dependencies' : [ - libcrypt, libintl, libpam_misc, libpam, diff --git a/src/shared/libcrypt-util.c b/src/shared/libcrypt-util.c index e8b0b72e353..85069314be4 100644 --- a/src/shared/libcrypt-util.c +++ b/src/shared/libcrypt-util.c @@ -5,6 +5,7 @@ #endif #include "alloc-util.h" +#include "dlfcn-util.h" #include "errno-util.h" #include "libcrypt-util.h" #include "log.h" @@ -12,19 +13,79 @@ #include "strv.h" #if HAVE_LIBCRYPT +static void *libcrypt_dl = NULL; + +static DLSYM_PROTOTYPE(crypt_gensalt_ra) = NULL; +static DLSYM_PROTOTYPE(crypt_preferred_method) = NULL; +static DLSYM_PROTOTYPE(crypt_ra) = NULL; + +int dlopen_libcrypt(void) { +#ifdef __GLIBC__ + static int cached = 0; + int r; + + if (libcrypt_dl) + return 0; /* Already loaded */ + + if (cached < 0) + return cached; /* Already tried, and failed. */ + + /* Several distributions like Debian/Ubuntu and OpenSUSE provide libxcrypt as libcrypt.so.1, + * while others like Fedora/CentOS and Arch provide it as libcrypt.so.2. */ + ELF_NOTE_DLOPEN("crypt", + "Support for hashing passwords", + ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + "libcrypt.so.2", "libcrypt.so.1"); + + _cleanup_(dlclosep) void *dl = NULL; + r = dlopen_safe("libcrypt.so.2", &dl, /* reterr_dlerror= */ NULL); + if (r < 0) { + const char *dle = NULL; + r = dlopen_safe("libcrypt.so.1", &dl, &dle); + if (r < 0) { + log_debug_errno(r, "libcrypt.so.2/libcrypt.so.1 is not available: %s", dle ?: STRERROR(r)); + return (cached = -EOPNOTSUPP); /* turn into recognizable error */ + } + log_debug("Loaded 'libcrypt.so.1' via dlopen()"); + } else + log_debug("Loaded 'libcrypt.so.2' via dlopen()"); + + r = dlsym_many_or_warn( + dl, LOG_DEBUG, + DLSYM_ARG(crypt_gensalt_ra), + DLSYM_ARG(crypt_preferred_method), + DLSYM_ARG(crypt_ra)); + if (r < 0) + return (cached = r); + + libcrypt_dl = TAKE_PTR(dl); +#else + libcrypt_dl = NULL; + sym_crypt_gensalt_ra = missing_crypt_gensalt_ra; + sym_crypt_preferred_method = missing_crypt_preferred_method; + sym_crypt_ra = missing_crypt_ra; +#endif + return 0; +} + int make_salt(char **ret) { const char *e; char *salt; + int r; assert(ret); + r = dlopen_libcrypt(); + if (r < 0) + return r; + e = secure_getenv("SYSTEMD_CRYPT_PREFIX"); if (!e) - e = crypt_preferred_method(); + e = sym_crypt_preferred_method(); log_debug("Generating salt for hash prefix: %s", e); - salt = crypt_gensalt_ra(e, 0, NULL, 0); + salt = sym_crypt_gensalt_ra(e, 0, NULL, 0); if (!salt) return -errno; @@ -46,7 +107,7 @@ int hash_password(const char *password, char **ret) { return log_debug_errno(r, "Failed to generate salt: %m"); errno = 0; - p = crypt_ra(password, salt, &cd_data, &cd_size); + p = sym_crypt_ra(password, salt, &cd_data, &cd_size); if (!p) return log_debug_errno(errno_or_else(SYNTHETIC_ERRNO(EINVAL)), "crypt_ra() failed: %m"); @@ -55,14 +116,18 @@ int hash_password(const char *password, char **ret) { int test_password_one(const char *hashed_password, const char *password) { _cleanup_(erase_and_freep) void *cd_data = NULL; - int cd_size = 0; + int r, cd_size = 0; const char *k; assert(hashed_password); assert(password); + r = dlopen_libcrypt(); + if (r < 0) + return r; + errno = 0; - k = crypt_ra(password, hashed_password, &cd_data, &cd_size); + k = sym_crypt_ra(password, hashed_password, &cd_data, &cd_size); if (!k) { if (errno == ENOMEM) return -ENOMEM; diff --git a/src/shared/libcrypt-util.h b/src/shared/libcrypt-util.h index 626668c710f..3f79916cbc0 100644 --- a/src/shared/libcrypt-util.h +++ b/src/shared/libcrypt-util.h @@ -4,6 +4,7 @@ #include "shared-forward.h" #if HAVE_LIBCRYPT +int dlopen_libcrypt(void); int make_salt(char **ret); int hash_password(const char *password, char **ret); int test_password_one(const char *hashed_password, const char *password); @@ -11,6 +12,9 @@ int test_password_many(char **hashed_password, const char *password); #else +static inline int dlopen_libcrypt(void) { + return -EOPNOTSUPP; +} static inline int hash_password(const char *password, char **ret) { return -EOPNOTSUPP; } diff --git a/src/shared/meson.build b/src/shared/meson.build index e0b86e0b81c..36d38f4ed9e 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -367,7 +367,7 @@ libshared_deps = [threads, libaudit_cflags, libblkid_cflags, libbpf_cflags, - libcrypt, + libcrypt_cflags, libcryptsetup_cflags, libdl, libdw_cflags, diff --git a/src/test/meson.build b/src/test/meson.build index 7b77f3bd587..6a26eb3f87c 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -345,7 +345,6 @@ executables += [ }, test_template + { 'sources' : files('test-libcrypt-util.c'), - 'dependencies' : libcrypt, 'conditions' : ['HAVE_LIBCRYPT'], 'timeout' : 120, }, diff --git a/src/test/test-dlopen-so.c b/src/test/test-dlopen-so.c index 47b8470ffb5..10b8da1e9ca 100644 --- a/src/test/test-dlopen-so.c +++ b/src/test/test-dlopen-so.c @@ -11,6 +11,7 @@ #include "idn-util.h" #include "libarchive-util.h" #include "libaudit-util.h" +#include "libcrypt-util.h" #include "libfido2-util.h" #include "libmount-util.h" #include "main-func.h" @@ -52,6 +53,7 @@ static int run(int argc, char **argv) { ASSERT_DLOPEN(dlopen_libarchive, HAVE_LIBARCHIVE); ASSERT_DLOPEN(dlopen_libaudit, HAVE_AUDIT); ASSERT_DLOPEN(dlopen_libblkid, HAVE_BLKID); + ASSERT_DLOPEN(dlopen_libcrypt, HAVE_LIBCRYPT); ASSERT_DLOPEN(dlopen_libfido2, HAVE_LIBFIDO2); ASSERT_DLOPEN(dlopen_libkmod, HAVE_KMOD); ASSERT_DLOPEN(dlopen_libmount, HAVE_LIBMOUNT); diff --git a/src/test/test-libcrypt-util.c b/src/test/test-libcrypt-util.c index 92142fc9efa..792b2b2ef90 100644 --- a/src/test/test-libcrypt-util.c +++ b/src/test/test-libcrypt-util.c @@ -1,15 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include - #include "libcrypt-util.h" #include "strv.h" #include "tests.h" -TEST(crypt_preferred_method) { - log_info("crypt_preferred_method: %s", crypt_preferred_method()); -} - TEST(make_salt) { _cleanup_strv_free_ char **l = NULL; -- 2.47.3