]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
libcrypt-util: turn into dlopen() dependency 38974/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 25 Oct 2025 04:41:33 +0000 (13:41 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 2 Jan 2026 04:06:14 +0000 (13:06 +0900)
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
meson.build
src/firstboot/meson.build
src/home/meson.build
src/shared/libcrypt-util.c
src/shared/libcrypt-util.h
src/shared/meson.build
src/test/meson.build
src/test/test-dlopen-so.c
src/test/test-libcrypt-util.c

diff --git a/README b/README
index 0fd153bebbbfc065fe027bb7d8f1a5c56e3e809a..2d6d30dc78e11604bd180d26ae1454b23ff9c304 100644 (file)
--- 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)
index 4f98dbd9da50b3c34b8e182006d80c63382964f8..ff8cb279a8e320c4de9306123b87eb2c8c52c5a0 100644 (file)
@@ -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)
index 28c1d2703a8658b43edbc910bfafd2165e4e2a8e..e83eded6ada9695172aa707e68eea09b3e58df6e 100644 (file)
@@ -6,6 +6,5 @@ executables += [
                 'public' : true,
                 'conditions' : ['ENABLE_FIRSTBOOT'],
                 'sources' : files('firstboot.c'),
-                'dependencies' : libcrypt,
         },
 ]
index dbb374ce4a1248d2bb8e0e78c86fc2867eb20dcd..1efee1619ef9c1754fb53e44a3f4b1307f3f9fff 100644 (file)
@@ -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,
index e8b0b72e35394c5051ab428d0d14a403bd236035..85069314be4976bd36f1b5fb34553d9cb3a34bc5 100644 (file)
@@ -5,6 +5,7 @@
 #endif
 
 #include "alloc-util.h"
+#include "dlfcn-util.h"
 #include "errno-util.h"
 #include "libcrypt-util.h"
 #include "log.h"
 #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;
index 626668c710faa7914be3327ebe0468d17253772c..3f79916cbc0be40dab7e016c2a12fea8332f29ed 100644 (file)
@@ -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;
 }
index e0b86e0b81ce1713b8131d22799afc96c3225192..36d38f4ed9e02357648a3d09d401e428152e0aa6 100644 (file)
@@ -367,7 +367,7 @@ libshared_deps = [threads,
                   libaudit_cflags,
                   libblkid_cflags,
                   libbpf_cflags,
-                  libcrypt,
+                  libcrypt_cflags,
                   libcryptsetup_cflags,
                   libdl,
                   libdw_cflags,
index 7b77f3bd587e87ed0948f4d0a41549281e217b24..6a26eb3f87ce58c0d1f59395cb351c59f37da970 100644 (file)
@@ -345,7 +345,6 @@ executables += [
         },
         test_template + {
                 'sources' : files('test-libcrypt-util.c'),
-                'dependencies' : libcrypt,
                 'conditions' : ['HAVE_LIBCRYPT'],
                 'timeout' : 120,
         },
index 47b8470ffb5885a380806f451b24ef0cd675357e..10b8da1e9ca5c453526cb140d2568c7b515293a4 100644 (file)
@@ -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);
index 92142fc9efae53572cc3930c46b9840f98375a70..792b2b2ef90265a4b90c1af3ab8c973d09fd3aa4 100644 (file)
@@ -1,15 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <crypt.h>
-
 #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;