]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
home: make libpwquality dep a runtime dlopen() one
authorLennart Poettering <lennart@poettering.net>
Tue, 18 Aug 2020 07:56:56 +0000 (09:56 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 19 Aug 2020 08:03:24 +0000 (10:03 +0200)
Also, let's move the glue for this to src/shared/ so that we later can
reuse this in sysemd-firstboot.

Given that libpwquality is a more a leaf dependency, let's make it
runtime optional, so that downstream distros can downgrade their package
deps from Required to Recommended.

meson.build
src/home/homectl.c
src/home/homed-home.c
src/home/meson.build
src/home/pwquality-util.c [deleted file]
src/home/pwquality-util.h [deleted file]
src/home/user-record-pwquality.c [new file with mode: 0644]
src/home/user-record-pwquality.h [new file with mode: 0644]
src/shared/meson.build
src/shared/pwquality-util.c [new file with mode: 0644]
src/shared/pwquality-util.h [new file with mode: 0644]

index 227fe097a81f70c85d42265e00737b4b472c9717..47b2b5f4b129633e12a8fdd60b4f181fdbf843b0 100644 (file)
@@ -2173,8 +2173,7 @@ if conf.get('ENABLE_HOMED') == 1
                 link_with : [libshared],
                 dependencies : [threads,
                                 libcrypt,
-                                libopenssl,
-                                libpwquality],
+                                libopenssl],
                 install_rpath : rootlibexecdir,
                 install : true,
                 install_dir : rootlibexecdir)
@@ -2188,8 +2187,7 @@ if conf.get('ENABLE_HOMED') == 1
                                 libcrypt,
                                 libopenssl,
                                 libp11kit,
-                                libfido2,
-                                libpwquality],
+                                libfido2],
                 install_rpath : rootlibexecdir,
                 install : true,
                 install_dir : rootbindir)
index 33e262706d29350f6bd63cbaf732595108e3fe04..9d082ef0c05177f4ed9f92674032fbbce8365348 100644 (file)
@@ -30,6 +30,7 @@
 #include "rlimit-util.h"
 #include "spawn-polkit-agent.h"
 #include "terminal-util.h"
+#include "user-record-pwquality.h"
 #include "user-record-show.h"
 #include "user-record-util.h"
 #include "user-record.h"
@@ -1097,7 +1098,7 @@ static int create_home(int argc, char *argv[], void *userdata) {
 
                 /* If password quality enforcement is disabled, let's at least warn client side */
 
-                r = quality_check_password(hr, hr, &error);
+                r = user_record_quality_check_password(hr, hr, &error);
                 if (r < 0)
                         log_warning_errno(r, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error, r));
         }
index f0c157cb7d882a117e8d1923aaaea6b41aa0c7ac..45c2152531fe72adf2a211f1c6c550b3c07f62b5 100644 (file)
@@ -33,6 +33,7 @@
 #include "strv.h"
 #include "user-record-sign.h"
 #include "user-record-util.h"
+#include "user-record-pwquality.h"
 #include "user-record.h"
 #include "user-util.h"
 
@@ -1289,7 +1290,7 @@ int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
         if (h->record->enforce_password_policy == false)
                 log_debug("Password quality check turned off for account, skipping.");
         else {
-                r = quality_check_password(h->record, secret, error);
+                r = user_record_quality_check_password(h->record, secret, error);
                 if (r < 0)
                         return r;
         }
@@ -1640,7 +1641,7 @@ int home_passwd(Home *h,
         if (c->enforce_password_policy == false)
                 log_debug("Password quality check turned off for account, skipping.");
         else {
-                r = quality_check_password(c, merged_secret, error);
+                r = user_record_quality_check_password(c, merged_secret, error);
                 if (r < 0)
                         return r;
         }
index 797f3a3c6d0a3a9b772c493e85b65fa25a3acc02..fbf09501c4cde45851cc60d759854fe6026236f8 100644 (file)
@@ -50,8 +50,8 @@ systemd_homed_sources = files('''
         homed-varlink.c
         homed-varlink.h
         homed.c
-        pwquality-util.c
-        pwquality-util.h
+        user-record-pwquality.c
+        user-record-pwquality.h
         user-record-sign.c
         user-record-sign.h
         user-record-util.c
@@ -74,8 +74,8 @@ homectl_sources = files('''
         homectl-pkcs11.c
         homectl-pkcs11.h
         homectl.c
-        pwquality-util.c
-        pwquality-util.h
+        user-record-pwquality.c
+        user-record-pwquality.h
         user-record-util.c
         user-record-util.h
 '''.split())
diff --git a/src/home/pwquality-util.c b/src/home/pwquality-util.c
deleted file mode 100644 (file)
index fbf6f6c..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#include <unistd.h>
-
-#if HAVE_PWQUALITY
-/* pwquality.h uses size_t but doesn't include sys/types.h on its own */
-#include <sys/types.h>
-#include <pwquality.h>
-#endif
-
-#include "bus-common-errors.h"
-#include "home-util.h"
-#include "memory-util.h"
-#include "pwquality-util.h"
-#include "strv.h"
-
-#if HAVE_PWQUALITY
-DEFINE_TRIVIAL_CLEANUP_FUNC(pwquality_settings_t*, pwquality_free_settings);
-
-static void pwquality_maybe_disable_dictionary(
-                pwquality_settings_t *pwq) {
-
-        char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
-        const char *path;
-        int r;
-
-        r = pwquality_get_str_value(pwq, PWQ_SETTING_DICT_PATH, &path);
-        if (r < 0) {
-                log_warning("Failed to read libpwquality dictionary path, ignoring: %s", pwquality_strerror(buf, sizeof(buf), r, NULL));
-                return;
-        }
-
-        // REMOVE THIS AS SOON AS https://github.com/libpwquality/libpwquality/pull/21 IS MERGED AND RELEASED
-        if (isempty(path))
-                path = "/usr/share/cracklib/pw_dict.pwd.gz";
-
-        if (isempty(path)) {
-                log_warning("Weird, no dictionary file configured, ignoring.");
-                return;
-        }
-
-        if (access(path, F_OK) >= 0)
-                return;
-
-        if (errno != ENOENT) {
-                log_warning_errno(errno, "Failed to check if dictionary file %s exists, ignoring: %m", path);
-                return;
-        }
-
-        r = pwquality_set_int_value(pwq, PWQ_SETTING_DICT_CHECK, 0);
-        if (r < 0) {
-                log_warning("Failed to disable libpwquality dictionary check, ignoring: %s", pwquality_strerror(buf, sizeof(buf), r, NULL));
-                return;
-        }
-}
-
-int quality_check_password(
-                UserRecord *hr,
-                UserRecord *secret,
-                sd_bus_error *error) {
-
-        _cleanup_(pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
-        char buf[PWQ_MAX_ERROR_MESSAGE_LEN], **pp;
-        void *auxerror;
-        int r;
-
-        assert(hr);
-        assert(secret);
-
-        pwq = pwquality_default_settings();
-        if (!pwq)
-                return log_oom();
-
-        r = pwquality_read_config(pwq, NULL, &auxerror);
-        if (r < 0)
-                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to read libpwquality configuration, ignoring: %s",
-                                  pwquality_strerror(buf, sizeof(buf), r, auxerror));
-
-        pwquality_maybe_disable_dictionary(pwq);
-
-        /* This is a bit more complex than one might think at first. pwquality_check() would like to know the
-         * old password to make security checks. We support arbitrary numbers of passwords however, hence we
-         * call the function once for each combination of old and new password. */
-
-        /* Iterate through all new passwords */
-        STRV_FOREACH(pp, secret->password) {
-                bool called = false;
-                char **old;
-
-                r = test_password_many(hr->hashed_password, *pp);
-                if (r < 0)
-                        return r;
-                if (r == 0) /* This is an old password as it isn't listed in the hashedPassword field, skip it */
-                        continue;
-
-                /* Check this password against all old passwords */
-                STRV_FOREACH(old, secret->password) {
-
-                        if (streq(*pp, *old))
-                                continue;
-
-                        r = test_password_many(hr->hashed_password, *old);
-                        if (r < 0)
-                                return r;
-                        if (r > 0) /* This is a new password, not suitable as old password */
-                                continue;
-
-                        r = pwquality_check(pwq, *pp, *old, hr->user_name, &auxerror);
-                        if (r < 0)
-                                return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
-                                                         pwquality_strerror(buf, sizeof(buf), r, auxerror));
-
-                        called = true;
-                }
-
-                if (called)
-                        continue;
-
-                /* If there are no old passwords, let's call pwquality_check() without any. */
-                r = pwquality_check(pwq, *pp, NULL, hr->user_name, &auxerror);
-                if (r < 0)
-                        return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
-                                                 pwquality_strerror(buf, sizeof(buf), r, auxerror));
-        }
-
-        return 0;
-}
-
-#define N_SUGGESTIONS 6
-
-int suggest_passwords(void) {
-        _cleanup_(pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
-        _cleanup_strv_free_erase_ char **suggestions = NULL;
-        _cleanup_(erase_and_freep) char *joined = NULL;
-        char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
-        void *auxerror;
-        size_t i;
-        int r;
-
-        pwq = pwquality_default_settings();
-        if (!pwq)
-                return log_oom();
-
-        r = pwquality_read_config(pwq, NULL, &auxerror);
-        if (r < 0)
-                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to read libpwquality configuration, ignoring: %s",
-                                  pwquality_strerror(buf, sizeof(buf), r, auxerror));
-
-        pwquality_maybe_disable_dictionary(pwq);
-
-        suggestions = new0(char*, N_SUGGESTIONS+1);
-        if (!suggestions)
-                return log_oom();
-
-        for (i = 0; i < N_SUGGESTIONS; i++) {
-                r = pwquality_generate(pwq, 64, suggestions + i);
-                if (r < 0)
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate password, ignoring: %s",
-                                               pwquality_strerror(buf, sizeof(buf), r, NULL));
-        }
-
-        joined = strv_join(suggestions, " ");
-        if (!joined)
-                return log_oom();
-
-        log_info("Password suggestions: %s", joined);
-        return 0;
-}
-
-#else
-
-int quality_check_password(
-                UserRecord *hr,
-                UserRecord *secret,
-                sd_bus_error *error) {
-
-        assert(hr);
-        assert(secret);
-
-        return 0;
-}
-
-int suggest_passwords(void) {
-        return 0;
-}
-#endif
diff --git a/src/home/pwquality-util.h b/src/home/pwquality-util.h
deleted file mode 100644 (file)
index d61c04c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-#pragma once
-
-#include "sd-bus.h"
-#include "user-record.h"
-
-int quality_check_password(UserRecord *hr, UserRecord *secret, sd_bus_error *error);
-
-int suggest_passwords(void);
diff --git a/src/home/user-record-pwquality.c b/src/home/user-record-pwquality.c
new file mode 100644 (file)
index 0000000..a5d632c
--- /dev/null
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-common-errors.h"
+#include "errno-util.h"
+#include "home-util.h"
+#include "pwquality-util.h"
+#include "strv.h"
+#include "user-record-pwquality.h"
+#include "user-record-util.h"
+
+#if HAVE_PWQUALITY
+
+int user_record_quality_check_password(
+                UserRecord *hr,
+                UserRecord *secret,
+                sd_bus_error *error) {
+
+        _cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
+        char buf[PWQ_MAX_ERROR_MESSAGE_LEN], **pp;
+        void *auxerror;
+        int r;
+
+        assert(hr);
+        assert(secret);
+
+        r = pwq_allocate_context(&pwq);
+        if (ERRNO_IS_NOT_SUPPORTED(r))
+                return 0;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to allocate libpwquality context: %m");
+
+        /* This is a bit more complex than one might think at first. pwquality_check() would like to know the
+         * old password to make security checks. We support arbitrary numbers of passwords however, hence we
+         * call the function once for each combination of old and new password. */
+
+        /* Iterate through all new passwords */
+        STRV_FOREACH(pp, secret->password) {
+                bool called = false;
+                char **old;
+
+                r = test_password_many(hr->hashed_password, *pp);
+                if (r < 0)
+                        return r;
+                if (r == 0) /* This is an old password as it isn't listed in the hashedPassword field, skip it */
+                        continue;
+
+                /* Check this password against all old passwords */
+                STRV_FOREACH(old, secret->password) {
+
+                        if (streq(*pp, *old))
+                                continue;
+
+                        r = test_password_many(hr->hashed_password, *old);
+                        if (r < 0)
+                                return r;
+                        if (r > 0) /* This is a new password, not suitable as old password */
+                                continue;
+
+                        r = sym_pwquality_check(pwq, *pp, *old, hr->user_name, &auxerror);
+                        if (r < 0)
+                                return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
+                                                         sym_pwquality_strerror(buf, sizeof(buf), r, auxerror));
+
+                        called = true;
+                }
+
+                if (called)
+                        continue;
+
+                /* If there are no old passwords, let's call pwquality_check() without any. */
+                r = sym_pwquality_check(pwq, *pp, NULL, hr->user_name, &auxerror);
+                if (r < 0)
+                        return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
+                                                 sym_pwquality_strerror(buf, sizeof(buf), r, auxerror));
+        }
+
+        return 1;
+}
+
+#else
+
+int user_record_quality_check_password(
+                UserRecord *hr,
+                UserRecord *secret,
+                sd_bus_error *error) {
+
+        return 0;
+}
+
+#endif
diff --git a/src/home/user-record-pwquality.h b/src/home/user-record-pwquality.h
new file mode 100644 (file)
index 0000000..a37d369
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+#include "user-record.h"
+
+int user_record_quality_check_password(UserRecord *hr, UserRecord *secret, sd_bus_error *error);
index 0da733c3fe7b3e61c53970834781151d3bb2340f..d192a4d8d7fcbf0f5939f608994d1610cf272631 100644 (file)
@@ -191,6 +191,8 @@ shared_sources = files('''
         pretty-print.h
         ptyfwd.c
         ptyfwd.h
+        pwquality-util.c
+        pwquality-util.h
         reboot-util.c
         reboot-util.h
         resize-fs.c
diff --git a/src/shared/pwquality-util.c b/src/shared/pwquality-util.c
new file mode 100644 (file)
index 0000000..799c39f
--- /dev/null
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <unistd.h>
+
+#include "dlfcn-util.h"
+#include "errno-util.h"
+#include "log.h"
+#include "macro.h"
+#include "memory-util.h"
+#include "pwquality-util.h"
+#include "strv.h"
+
+#if HAVE_PWQUALITY
+
+static void *pwquality_dl = NULL;
+
+int (*sym_pwquality_check)(pwquality_settings_t *pwq, const char *password, const char *oldpassword, const char *user, void **auxerror);
+pwquality_settings_t *(*sym_pwquality_default_settings)(void);
+void (*sym_pwquality_free_settings)(pwquality_settings_t *pwq);
+int (*sym_pwquality_generate)(pwquality_settings_t *pwq, int entropy_bits, char **password);
+int (*sym_pwquality_get_str_value)(pwquality_settings_t *pwq, int setting, const char **value);
+int (*sym_pwquality_read_config)(pwquality_settings_t *pwq, const char *cfgfile, void **auxerror);
+int (*sym_pwquality_set_int_value)(pwquality_settings_t *pwq, int setting, int value);
+const char* (*sym_pwquality_strerror)(char *buf, size_t len, int errcode, void *auxerror);
+
+int dlopen_pwquality(void) {
+        _cleanup_(dlclosep) void *dl = NULL;
+        int r;
+
+        if (pwquality_dl)
+                return 0; /* Already loaded */
+
+        dl = dlopen("libpwquality.so.1", RTLD_LAZY);
+        if (!dl)
+                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "libpwquality support is not installed: %s", dlerror());
+
+        r = dlsym_many_and_warn(
+                        dl,
+                        LOG_DEBUG,
+                        &sym_pwquality_check, "pwquality_check",
+                        &sym_pwquality_default_settings, "pwquality_default_settings",
+                        &sym_pwquality_free_settings, "pwquality_free_settings",
+                        &sym_pwquality_generate, "pwquality_generate",
+                        &sym_pwquality_get_str_value, "pwquality_get_str_value",
+                        &sym_pwquality_read_config, "pwquality_read_config",
+                        &sym_pwquality_set_int_value, "pwquality_set_int_value",
+                        &sym_pwquality_strerror, "pwquality_strerror",
+                        NULL);
+        if (r < 0)
+                return r;
+
+        /* Note that we never release the reference here, because there's no real reason to, after all this
+         * was traditionally a regular shared library dependency which lives forever too. */
+        pwquality_dl = TAKE_PTR(dl);
+        return 1;
+}
+
+void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq) {
+        char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
+        const char *path;
+        int r;
+
+        assert(pwq);
+
+        r = sym_pwquality_get_str_value(pwq, PWQ_SETTING_DICT_PATH, &path);
+        if (r < 0) {
+                log_debug("Failed to read libpwquality dictionary path, ignoring: %s",
+                          sym_pwquality_strerror(buf, sizeof(buf), r, NULL));
+                return;
+        }
+
+        // REMOVE THIS AS SOON AS https://github.com/libpwquality/libpwquality/pull/21 IS MERGED AND RELEASED
+        if (isempty(path))
+                path = "/usr/share/cracklib/pw_dict.pwd.gz";
+
+        if (isempty(path)) {
+                log_debug("Weird, no dictionary file configured, ignoring.");
+                return;
+        }
+
+        if (access(path, F_OK) >= 0)
+                return;
+
+        if (errno != ENOENT) {
+                log_debug_errno(errno, "Failed to check if dictionary file %s exists, ignoring: %m", path);
+                return;
+        }
+
+        r = sym_pwquality_set_int_value(pwq, PWQ_SETTING_DICT_CHECK, 0);
+        if (r < 0)
+                log_debug("Failed to disable libpwquality dictionary check, ignoring: %s",
+                          sym_pwquality_strerror(buf, sizeof(buf), r, NULL));
+}
+
+int pwq_allocate_context(pwquality_settings_t **ret) {
+        _cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
+        char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
+        void *auxerror;
+        int r;
+
+        assert(ret);
+
+        r = dlopen_pwquality();
+        if (r < 0)
+                return r;
+
+        pwq = sym_pwquality_default_settings();
+        if (!pwq)
+                return -ENOMEM;
+
+        r = sym_pwquality_read_config(pwq, NULL, &auxerror);
+        if (r < 0)
+                log_debug("Failed to read libpwquality configuration, ignoring: %s",
+                          sym_pwquality_strerror(buf, sizeof(buf), r, auxerror));
+
+        pwq_maybe_disable_dictionary(pwq);
+
+        *ret = TAKE_PTR(pwq);
+        return 0;
+}
+
+#define N_SUGGESTIONS 6
+
+int suggest_passwords(void) {
+        _cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
+        _cleanup_strv_free_erase_ char **suggestions = NULL;
+        _cleanup_(erase_and_freep) char *joined = NULL;
+        char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
+        size_t i;
+        int r;
+
+        r = pwq_allocate_context(&pwq);
+        if (ERRNO_IS_NOT_SUPPORTED(r))
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate libpwquality context: %m");
+
+        suggestions = new0(char*, N_SUGGESTIONS+1);
+        if (!suggestions)
+                return log_oom();
+
+        for (i = 0; i < N_SUGGESTIONS; i++) {
+                r = sym_pwquality_generate(pwq, 64, suggestions + i);
+                if (r < 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate password, ignoring: %s",
+                                               sym_pwquality_strerror(buf, sizeof(buf), r, NULL));
+        }
+
+        joined = strv_join(suggestions, " ");
+        if (!joined)
+                return log_oom();
+
+        log_info("Password suggestions: %s", joined);
+        return 1;
+}
+
+#endif
diff --git a/src/shared/pwquality-util.h b/src/shared/pwquality-util.h
new file mode 100644 (file)
index 0000000..2ef34da
--- /dev/null
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "macro.h"
+
+#if HAVE_PWQUALITY
+/* pwquality.h uses size_t but doesn't include sys/types.h on its own */
+#include <sys/types.h>
+#include <pwquality.h>
+
+extern int (*sym_pwquality_check)(pwquality_settings_t *pwq, const char *password, const char *oldpassword, const char *user, void **auxerror);
+extern pwquality_settings_t *(*sym_pwquality_default_settings)(void);
+extern void (*sym_pwquality_free_settings)(pwquality_settings_t *pwq);
+extern int (*sym_pwquality_generate)(pwquality_settings_t *pwq, int entropy_bits, char **password);
+extern int (*sym_pwquality_get_str_value)(pwquality_settings_t *pwq, int setting, const char **value);
+extern int (*sym_pwquality_read_config)(pwquality_settings_t *pwq, const char *cfgfile, void **auxerror);
+extern int (*sym_pwquality_set_int_value)(pwquality_settings_t *pwq, int setting, int value);
+extern const char* (*sym_pwquality_strerror)(char *buf, size_t len, int errcode, void *auxerror);
+
+int dlopen_pwquality(void);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(pwquality_settings_t*, sym_pwquality_free_settings);
+
+void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq);
+int pwq_allocate_context(pwquality_settings_t **ret);
+int suggest_passwords(void);
+
+#else
+
+static inline int suggest_passwords(void) {
+        return 0;
+}
+
+#endif