From 29dd2e253c74c7ab2fed6fb6a67a87089197253f Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Wed, 5 Jul 2023 08:00:00 +0000 Subject: [PATCH 1/7] pwquality: fix use of ERRNO_IS_NOT_SUPPORTED Given that ERRNO_IS_*() also match positive values, call ERRNO_IS_NOT_SUPPORTED() only if the value returned by pwq_allocate_context() is negative. --- src/shared/pwquality-util.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/shared/pwquality-util.c b/src/shared/pwquality-util.c index 839cf4caf9..5deb9324ae 100644 --- a/src/shared/pwquality-util.c +++ b/src/shared/pwquality-util.c @@ -107,10 +107,11 @@ int suggest_passwords(void) { int r; r = pwq_allocate_context(&pwq); - if (ERRNO_IS_NOT_SUPPORTED(r)) - return 0; - if (r < 0) + if (r < 0) { + if (ERRNO_IS_NOT_SUPPORTED(r)) + return 0; return log_error_errno(r, "Failed to allocate libpwquality context: %m"); + } suggestions = new0(char*, N_SUGGESTIONS+1); if (!suggestions) @@ -140,10 +141,11 @@ int quality_check_password(const char *password, const char *username, char **re assert(password); r = pwq_allocate_context(&pwq); - if (ERRNO_IS_NOT_SUPPORTED(r)) - return 0; - if (r < 0) + if (r < 0) { + if (ERRNO_IS_NOT_SUPPORTED(r)) + return 0; return log_debug_errno(r, "Failed to allocate libpwquality context: %m"); + } r = sym_pwquality_check(pwq, password, NULL, username, &auxerror); if (r < 0) { From 7fc3f9c032cad0690c08fcdee190d83f1c7a6d53 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Wed, 5 Jul 2023 08:00:00 +0000 Subject: [PATCH 2/7] pwquality: fix quality_check_password return value quality_check_password() used to return the same value 0 in two different cases: when pwq_allocate_context() failed with a ERRNO_IS_NOT_SUPPORTED() code, and when pwquality_check() rejected the password. As result, users of quality_check_password() used to report password weakness also in case when the underlying library was not available. Fix this by changing quality_check_password() to forward the ERRNO_IS_NOT_SUPPORTED() code to its callers, and change the callers to handle this case gracefully. --- src/cryptenroll/cryptenroll-password.c | 9 +++++++-- src/firstboot/firstboot.c | 9 +++++++-- src/shared/pwquality-util.c | 5 +---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c index d636db266e..de72278394 100644 --- a/src/cryptenroll/cryptenroll-password.c +++ b/src/cryptenroll/cryptenroll-password.c @@ -3,6 +3,7 @@ #include "ask-password-api.h" #include "cryptenroll-password.h" #include "env-util.h" +#include "errno-util.h" #include "escape.h" #include "memory-util.h" #include "pwquality-util.h" @@ -156,8 +157,12 @@ int enroll_password( } r = quality_check_password(new_password, NULL, &error); - if (r < 0) - return log_error_errno(r, "Failed to check password for quality: %m"); + if (r < 0) { + if (ERRNO_IS_NOT_SUPPORTED(r)) + log_warning("Password quality check is not supported, proceeding anyway."); + else + return log_error_errno(r, "Failed to check password quality: %m"); + } if (r == 0) log_warning("Specified password does not pass quality checks (%s), proceeding anyway.", error); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 076e06e821..019b7d70af 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -19,6 +19,7 @@ #include "creds-util.h" #include "dissect-image.h" #include "env-file.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -790,8 +791,12 @@ static int prompt_root_password(int rfd) { } r = quality_check_password(*a, "root", &error); - if (r < 0) - return log_error_errno(r, "Failed to check quality of password: %m"); + if (r < 0) { + if (ERRNO_IS_NOT_SUPPORTED(r)) + log_warning("Password quality check is not supported, proceeding anyway."); + else + return log_error_errno(r, "Failed to check password quality: %m"); + } if (r == 0) log_warning("Password is weak, accepting anyway: %s", error); diff --git a/src/shared/pwquality-util.c b/src/shared/pwquality-util.c index 5deb9324ae..d25104c38f 100644 --- a/src/shared/pwquality-util.c +++ b/src/shared/pwquality-util.c @@ -141,11 +141,8 @@ int quality_check_password(const char *password, const char *username, char **re assert(password); r = pwq_allocate_context(&pwq); - if (r < 0) { - if (ERRNO_IS_NOT_SUPPORTED(r)) - return 0; + if (r < 0) return log_debug_errno(r, "Failed to allocate libpwquality context: %m"); - } r = sym_pwquality_check(pwq, password, NULL, username, &auxerror); if (r < 0) { From 0351d560a25cb7caf94c1f9c1af1f5e9b6b6a01a Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Wed, 5 Jul 2023 18:00:00 +0000 Subject: [PATCH 3/7] pwquality: do not log password suggestions As logging password suggestions might leak sensitive information, print it instead. Suggested-by: Yu Watanabe --- src/shared/pwquality-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/pwquality-util.c b/src/shared/pwquality-util.c index d25104c38f..34d3e35330 100644 --- a/src/shared/pwquality-util.c +++ b/src/shared/pwquality-util.c @@ -128,7 +128,7 @@ int suggest_passwords(void) { if (!joined) return log_oom(); - log_info("Password suggestions: %s", joined); + printf("Password suggestions: %s\n", joined); return 1; } From bc0ef0e2c0a116731f8535802ca0d077212e3c1f Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 6 Jul 2023 08:00:00 +0000 Subject: [PATCH 4/7] pwquality: add old password argument to quality_check_password This would allow to use quality_check_password() in user_record_quality_check_password() which still uses sym_pwquality_check() directly. --- src/cryptenroll/cryptenroll-password.c | 2 +- src/firstboot/firstboot.c | 2 +- src/shared/pwquality-util.c | 4 ++-- src/shared/pwquality-util.h | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c index de72278394..44e6f0b227 100644 --- a/src/cryptenroll/cryptenroll-password.c +++ b/src/cryptenroll/cryptenroll-password.c @@ -156,7 +156,7 @@ int enroll_password( } } - r = quality_check_password(new_password, NULL, &error); + r = quality_check_password(new_password, /* old */ NULL, /* user */ NULL, &error); if (r < 0) { if (ERRNO_IS_NOT_SUPPORTED(r)) log_warning("Password quality check is not supported, proceeding anyway."); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 019b7d70af..501f074c94 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -790,7 +790,7 @@ static int prompt_root_password(int rfd) { break; } - r = quality_check_password(*a, "root", &error); + r = quality_check_password(*a, /* old */ NULL, "root", &error); if (r < 0) { if (ERRNO_IS_NOT_SUPPORTED(r)) log_warning("Password quality check is not supported, proceeding anyway."); diff --git a/src/shared/pwquality-util.c b/src/shared/pwquality-util.c index 34d3e35330..450208319d 100644 --- a/src/shared/pwquality-util.c +++ b/src/shared/pwquality-util.c @@ -132,7 +132,7 @@ int suggest_passwords(void) { return 1; } -int quality_check_password(const char *password, const char *username, char **ret_error) { +int quality_check_password(const char *password, const char *old, const char *username, char **ret_error) { _cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL; char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; void *auxerror; @@ -144,7 +144,7 @@ int quality_check_password(const char *password, const char *username, char **re if (r < 0) return log_debug_errno(r, "Failed to allocate libpwquality context: %m"); - r = sym_pwquality_check(pwq, password, NULL, username, &auxerror); + r = sym_pwquality_check(pwq, password, old, username, &auxerror); if (r < 0) { if (ret_error) { diff --git a/src/shared/pwquality-util.h b/src/shared/pwquality-util.h index 877f439d11..4e18f39f78 100644 --- a/src/shared/pwquality-util.h +++ b/src/shared/pwquality-util.h @@ -24,7 +24,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(pwquality_settings_t*, sym_pwquality_free_setti void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq); int pwq_allocate_context(pwquality_settings_t **ret); int suggest_passwords(void); -int quality_check_password(const char *password, const char *username, char **ret_error); +int quality_check_password(const char *password, const char *old, const char *username, char **ret_error); #else @@ -32,7 +32,7 @@ static inline int suggest_passwords(void) { return 0; } -static inline int quality_check_password(const char *password, const char *username, char **ret_error) { +static inline int quality_check_password(const char *password, const char *old, const char *username, char **ret_error) { if (ret_error) *ret_error = NULL; return 1; /* all good */ From 6377f57fa74fdafcab2ca9a8b8fa474253fa8fa9 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 6 Jul 2023 08:00:00 +0000 Subject: [PATCH 5/7] homed: change user_record_quality_check_password to use quality_check_password With this change, the only direct users of libpwquality functions are those defined in pwquality-util. --- src/home/user-record-pwquality.c | 36 +++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/home/user-record-pwquality.c b/src/home/user-record-pwquality.c index 609e620511..7e18773232 100644 --- a/src/home/user-record-pwquality.c +++ b/src/home/user-record-pwquality.c @@ -16,21 +16,13 @@ int user_record_quality_check_password( UserRecord *secret, sd_bus_error *error) { - _cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL; - char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; - void *auxerror; + _cleanup_free_ char *auxerror = NULL; 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 + /* This is a bit more complex than one might think at first. quality_check_password() 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. */ @@ -56,10 +48,9 @@ int user_record_quality_check_password( 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)); + r = quality_check_password(*pp, *old, hr->user_name, &auxerror); + if (r <= 0) + goto error; called = true; } @@ -67,14 +58,21 @@ int user_record_quality_check_password( 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)); + /* If there are no old passwords, let's call quality_check_password() without any. */ + r = quality_check_password(*pp, /* old */ NULL, hr->user_name, &auxerror); + if (r <= 0) + goto error; } return 1; + +error: + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, + "Password too weak: %s", auxerror); + if (ERRNO_IS_NOT_SUPPORTED(r)) + return 0; + return log_debug_errno(r, "Failed to check password quality: %m"); } #else From d34b1823cace2b85436fc0ee79c0569d2f085f72 Mon Sep 17 00:00:00 2001 From: Egor Ignatov Date: Fri, 6 Aug 2021 11:40:34 +0300 Subject: [PATCH 6/7] shared: add password quality check abstraction layer to support both pwquality and passwdqc Co-authored-by: Dmitry V. Levin --- src/cryptenroll/cryptenroll-password.c | 4 +-- src/firstboot/firstboot.c | 4 +-- src/home/homectl.c | 6 ++--- src/home/homed-home.c | 7 +++-- src/home/meson.build | 4 +-- ...ality.c => user-record-password-quality.c} | 17 ++++++------ ...ality.h => user-record-password-quality.h} | 2 +- src/shared/meson.build | 2 +- ...il.c => password-quality-util-pwquality.c} | 11 +++----- ...il.h => password-quality-util-pwquality.h} | 16 +----------- src/shared/password-quality-util.h | 26 +++++++++++++++++++ src/test/test-dlopen-so.c | 2 +- 12 files changed, 54 insertions(+), 47 deletions(-) rename src/home/{user-record-pwquality.c => user-record-password-quality.c} (84%) rename src/home/{user-record-pwquality.h => user-record-password-quality.h} (68%) rename src/shared/{pwquality-util.c => password-quality-util-pwquality.c} (95%) rename src/shared/{pwquality-util.h => password-quality-util-pwquality.h} (72%) create mode 100644 src/shared/password-quality-util.h diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c index 44e6f0b227..499dbbc627 100644 --- a/src/cryptenroll/cryptenroll-password.c +++ b/src/cryptenroll/cryptenroll-password.c @@ -6,7 +6,7 @@ #include "errno-util.h" #include "escape.h" #include "memory-util.h" -#include "pwquality-util.h" +#include "password-quality-util.h" #include "strv.h" int load_volume_key_password( @@ -156,7 +156,7 @@ int enroll_password( } } - r = quality_check_password(new_password, /* old */ NULL, /* user */ NULL, &error); + r = check_password_quality(new_password, /* old */ NULL, /* user */ NULL, &error); if (r < 0) { if (ERRNO_IS_NOT_SUPPORTED(r)) log_warning("Password quality check is not supported, proceeding anyway."); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 501f074c94..b993739083 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -36,10 +36,10 @@ #include "os-util.h" #include "parse-argument.h" #include "parse-util.h" +#include "password-quality-util.h" #include "path-util.h" #include "pretty-print.h" #include "proc-cmdline.h" -#include "pwquality-util.h" #include "random-util.h" #include "smack-util.h" #include "string-util.h" @@ -790,7 +790,7 @@ static int prompt_root_password(int rfd) { break; } - r = quality_check_password(*a, /* old */ NULL, "root", &error); + r = check_password_quality(*a, /* old */ NULL, "root", &error); if (r < 0) { if (ERRNO_IS_NOT_SUPPORTED(r)) log_warning("Password quality check is not supported, proceeding anyway."); diff --git a/src/home/homectl.c b/src/home/homectl.c index d6e6bd0855..55323b44e1 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -30,18 +30,18 @@ #include "pager.h" #include "parse-argument.h" #include "parse-util.h" +#include "password-quality-util.h" #include "path-util.h" #include "percent-util.h" #include "pkcs11-util.h" #include "pretty-print.h" #include "process-util.h" -#include "pwquality-util.h" #include "rlimit-util.h" #include "spawn-polkit-agent.h" #include "terminal-util.h" #include "uid-alloc-range.h" #include "user-record.h" -#include "user-record-pwquality.h" +#include "user-record-password-quality.h" #include "user-record-show.h" #include "user-record-util.h" #include "user-util.h" @@ -1323,7 +1323,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 = user_record_quality_check_password(hr, hr, &error); + r = user_record_check_password_quality(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)); } diff --git a/src/home/homed-home.c b/src/home/homed-home.c index a79a719383..46d6fb589c 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -31,7 +31,6 @@ #include "mkdir.h" #include "path-util.h" #include "process-util.h" -#include "pwquality-util.h" #include "quota-util.h" #include "resize-fs.h" #include "set.h" @@ -40,7 +39,7 @@ #include "string-table.h" #include "strv.h" #include "uid-alloc-range.h" -#include "user-record-pwquality.h" +#include "user-record-password-quality.h" #include "user-record-sign.h" #include "user-record-util.h" #include "user-record.h" @@ -1513,7 +1512,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 = user_record_quality_check_password(h->record, secret, error); + r = user_record_check_password_quality(h->record, secret, error); if (r < 0) return r; } @@ -1888,7 +1887,7 @@ int home_passwd(Home *h, if (c->enforce_password_policy == false) log_debug("Password quality check turned off for account, skipping."); else { - r = user_record_quality_check_password(c, merged_secret, error); + r = user_record_check_password_quality(c, merged_secret, error); if (r < 0) return r; } diff --git a/src/home/meson.build b/src/home/meson.build index 475faaefea..ff3cf411fe 100644 --- a/src/home/meson.build +++ b/src/home/meson.build @@ -33,7 +33,7 @@ systemd_homed_sources = files( 'homed-operation.c', 'homed-varlink.c', 'homed.c', - 'user-record-pwquality.c', + 'user-record-password-quality.c', 'user-record-sign.c', 'user-record-util.c', ) @@ -52,7 +52,7 @@ homectl_sources = files( 'homectl-pkcs11.c', 'homectl-recovery-key.c', 'homectl.c', - 'user-record-pwquality.c', + 'user-record-password-quality.c', 'user-record-util.c', ) diff --git a/src/home/user-record-pwquality.c b/src/home/user-record-password-quality.c similarity index 84% rename from src/home/user-record-pwquality.c rename to src/home/user-record-password-quality.c index 7e18773232..5c2909688d 100644 --- a/src/home/user-record-pwquality.c +++ b/src/home/user-record-password-quality.c @@ -4,14 +4,14 @@ #include "errno-util.h" #include "home-util.h" #include "libcrypt-util.h" -#include "pwquality-util.h" +#include "password-quality-util.h" #include "strv.h" -#include "user-record-pwquality.h" +#include "user-record-password-quality.h" #include "user-record-util.h" #if HAVE_PWQUALITY -int user_record_quality_check_password( +int user_record_check_password_quality( UserRecord *hr, UserRecord *secret, sd_bus_error *error) { @@ -22,7 +22,7 @@ int user_record_quality_check_password( assert(hr); assert(secret); - /* This is a bit more complex than one might think at first. quality_check_password() would like to know the + /* This is a bit more complex than one might think at first. check_password_quality() 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. */ @@ -48,7 +48,7 @@ int user_record_quality_check_password( if (r > 0) /* This is a new password, not suitable as old password */ continue; - r = quality_check_password(*pp, *old, hr->user_name, &auxerror); + r = check_password_quality(*pp, *old, hr->user_name, &auxerror); if (r <= 0) goto error; @@ -58,12 +58,11 @@ int user_record_quality_check_password( if (called) continue; - /* If there are no old passwords, let's call quality_check_password() without any. */ - r = quality_check_password(*pp, /* old */ NULL, hr->user_name, &auxerror); + /* If there are no old passwords, let's call check_password_quality() without any. */ + r = check_password_quality(*pp, /* old */ NULL, hr->user_name, &auxerror); if (r <= 0) goto error; } - return 1; error: @@ -77,7 +76,7 @@ error: #else -int user_record_quality_check_password( +int user_record_check_password_quality( UserRecord *hr, UserRecord *secret, sd_bus_error *error) { diff --git a/src/home/user-record-pwquality.h b/src/home/user-record-password-quality.h similarity index 68% rename from src/home/user-record-pwquality.h rename to src/home/user-record-password-quality.h index b3b2690f7e..c7d6ec6046 100644 --- a/src/home/user-record-pwquality.h +++ b/src/home/user-record-password-quality.h @@ -4,4 +4,4 @@ #include "sd-bus.h" #include "user-record.h" -int user_record_quality_check_password(UserRecord *hr, UserRecord *secret, sd_bus_error *error); +int user_record_check_password_quality(UserRecord *hr, UserRecord *secret, sd_bus_error *error); diff --git a/src/shared/meson.build b/src/shared/meson.build index d643b2bd09..fe88731c32 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -128,11 +128,11 @@ shared_sources = files( 'pager.c', 'parse-argument.c', 'parse-helpers.c', + 'password-quality-util-pwquality.c', 'pcre2-util.c', 'pkcs11-util.c', 'pretty-print.c', 'ptyfwd.c', - 'pwquality-util.c', 'qrcode-util.c', 'quota-util.c', 'reboot-util.c', diff --git a/src/shared/pwquality-util.c b/src/shared/password-quality-util-pwquality.c similarity index 95% rename from src/shared/pwquality-util.c rename to src/shared/password-quality-util-pwquality.c index 450208319d..80f7d58e5d 100644 --- a/src/shared/pwquality-util.c +++ b/src/shared/password-quality-util-pwquality.c @@ -7,7 +7,7 @@ #include "log.h" #include "macro.h" #include "memory-util.h" -#include "pwquality-util.h" +#include "password-quality-util.h" #include "strv.h" #if HAVE_PWQUALITY @@ -36,7 +36,7 @@ int dlopen_pwquality(void) { DLSYM_ARG(pwquality_strerror)); } -void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq) { +static void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq) { char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; const char *path; int r; @@ -69,7 +69,7 @@ void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq) { sym_pwquality_strerror(buf, sizeof(buf), r, NULL)); } -int pwq_allocate_context(pwquality_settings_t **ret) { +static 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; @@ -96,8 +96,6 @@ int pwq_allocate_context(pwquality_settings_t **ret) { 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; @@ -132,7 +130,7 @@ int suggest_passwords(void) { return 1; } -int quality_check_password(const char *password, const char *old, const char *username, char **ret_error) { +int check_password_quality(const char *password, const char *old, const char *username, char **ret_error) { _cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL; char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; void *auxerror; @@ -146,7 +144,6 @@ int quality_check_password(const char *password, const char *old, const char *us r = sym_pwquality_check(pwq, password, old, username, &auxerror); if (r < 0) { - if (ret_error) { _cleanup_free_ char *e = NULL; diff --git a/src/shared/pwquality-util.h b/src/shared/password-quality-util-pwquality.h similarity index 72% rename from src/shared/pwquality-util.h rename to src/shared/password-quality-util-pwquality.h index 4e18f39f78..a420b0df2c 100644 --- a/src/shared/pwquality-util.h +++ b/src/shared/password-quality-util-pwquality.h @@ -21,21 +21,7 @@ int dlopen_pwquality(void); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(pwquality_settings_t*, sym_pwquality_free_settings, NULL); -void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq); -int pwq_allocate_context(pwquality_settings_t **ret); int suggest_passwords(void); -int quality_check_password(const char *password, const char *old, const char *username, char **ret_error); - -#else - -static inline int suggest_passwords(void) { - return 0; -} - -static inline int quality_check_password(const char *password, const char *old, const char *username, char **ret_error) { - if (ret_error) - *ret_error = NULL; - return 1; /* all good */ -} +int check_password_quality(const char *password, const char *old, const char *username, char **ret_error); #endif diff --git a/src/shared/password-quality-util.h b/src/shared/password-quality-util.h new file mode 100644 index 0000000000..a55727d1fb --- /dev/null +++ b/src/shared/password-quality-util.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#define N_SUGGESTIONS 6 + +#if HAVE_PWQUALITY + +#include "password-quality-util-pwquality.h" + +#else + +static inline int suggest_passwords(void) { + return 0; +} + +static inline int check_password_quality( + const char *password, + const char *old, + const char *username, + char **ret_error) { + if (ret_error) + *ret_error = NULL; + return 1; /* all good */ +} + +#endif diff --git a/src/test/test-dlopen-so.c b/src/test/test-dlopen-so.c index 55728c27c5..2d3b7744f6 100644 --- a/src/test/test-dlopen-so.c +++ b/src/test/test-dlopen-so.c @@ -10,9 +10,9 @@ #include "libfido2-util.h" #include "macro.h" #include "main-func.h" +#include "password-quality-util-pwquality.h" #include "pcre2-util.h" #include "pkcs11-util.h" -#include "pwquality-util.h" #include "qrcode-util.h" #include "tests.h" #include "tpm2-util.h" From 1d8aae432724a98af82fcca7c02f80793a7e0731 Mon Sep 17 00:00:00 2001 From: Egor Ignatov Date: Fri, 6 Aug 2021 11:41:01 +0300 Subject: [PATCH 7/7] shared: add libpasswdqc support Co-authored-by: Dmitry V. Levin Resolves: #15055 --- meson.build | 18 ++- meson_options.txt | 2 + src/home/user-record-password-quality.c | 2 +- src/shared/meson.build | 1 + src/shared/password-quality-util-passwdqc.c | 142 ++++++++++++++++++++ src/shared/password-quality-util-passwdqc.h | 23 ++++ src/shared/password-quality-util.h | 6 +- src/test/test-dlopen-so.c | 5 + 8 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 src/shared/password-quality-util-passwdqc.c create mode 100644 src/shared/password-quality-util-passwdqc.h diff --git a/meson.build b/meson.build index fa2f7e3544..3b29807e93 100644 --- a/meson.build +++ b/meson.build @@ -1189,8 +1189,13 @@ else endif conf.set10('HAVE_LIBFDISK', have) +want_passwdqc = get_option('passwdqc') want_pwquality = get_option('pwquality') -if want_pwquality != 'false' and not skip_deps +if want_passwdqc == 'true' and want_pwquality == 'true' + error('passwdqc and pwquality cannot be requested simultaneously') +endif + +if want_pwquality != 'false' and want_passwdqc != 'true' and not skip_deps libpwquality = dependency('pwquality', version : '>= 1.4.1', required : want_pwquality == 'true') @@ -1201,6 +1206,16 @@ else endif conf.set10('HAVE_PWQUALITY', have) +if not have and want_passwdqc != 'false' and not skip_deps + libpasswdqc = dependency('passwdqc', + required : want_passwdqc == 'true') + have = libpasswdqc.found() +else + have = false + libpasswdqc = [] +endif +conf.set10('HAVE_PASSWDQC', have) + want_seccomp = get_option('seccomp') if want_seccomp != 'false' and not skip_deps libseccomp = dependency('libseccomp', @@ -4940,6 +4955,7 @@ foreach tuple : [ ['microhttpd'], ['openssl'], ['p11kit'], + ['passwdqc'], ['pcre2'], ['pwquality'], ['qrencode'], diff --git a/meson_options.txt b/meson_options.txt index 46a74b7635..1909323850 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -381,6 +381,8 @@ option('xenctrl', type : 'combo', choices : ['auto', 'true', 'false'], description : 'support for Xen kexec') option('pam', type : 'combo', choices : ['auto', 'true', 'false'], description : 'PAM support') +option('passwdqc', type : 'combo', choices : ['auto', 'true', 'false'], + description : 'libpasswdqc support') option('pwquality', type : 'combo', choices : ['auto', 'true', 'false'], description : 'libpwquality support') option('microhttpd', type : 'combo', choices : ['auto', 'true', 'false'], diff --git a/src/home/user-record-password-quality.c b/src/home/user-record-password-quality.c index 5c2909688d..38f4acb0e7 100644 --- a/src/home/user-record-password-quality.c +++ b/src/home/user-record-password-quality.c @@ -9,7 +9,7 @@ #include "user-record-password-quality.h" #include "user-record-util.h" -#if HAVE_PWQUALITY +#if HAVE_PASSWDQC || HAVE_PWQUALITY int user_record_check_password_quality( UserRecord *hr, diff --git a/src/shared/meson.build b/src/shared/meson.build index fe88731c32..8fe02410d6 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -128,6 +128,7 @@ shared_sources = files( 'pager.c', 'parse-argument.c', 'parse-helpers.c', + 'password-quality-util-passwdqc.c', 'password-quality-util-pwquality.c', 'pcre2-util.c', 'pkcs11-util.c', diff --git a/src/shared/password-quality-util-passwdqc.c b/src/shared/password-quality-util-passwdqc.c new file mode 100644 index 0000000000..adfc14d740 --- /dev/null +++ b/src/shared/password-quality-util-passwdqc.c @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "dlfcn-util.h" +#include "errno-util.h" +#include "log.h" +#include "macro.h" +#include "memory-util.h" +#include "password-quality-util.h" +#include "strv.h" + +#if HAVE_PASSWDQC + +static void *passwdqc_dl = NULL; + +void (*sym_passwdqc_params_reset)(passwdqc_params_t *params); +int (*sym_passwdqc_params_load)(passwdqc_params_t *params, char **reason, const char *pathname); +int (*sym_passwdqc_params_parse)(passwdqc_params_t *params, char **reason, int argc, const char *const *argv); +void (*sym_passwdqc_params_free)(passwdqc_params_t *params); +const char *(*sym_passwdqc_check)(const passwdqc_params_qc_t *params, const char *newpass, const char *oldpass, const struct passwd *pw); +char *(*sym_passwdqc_random)(const passwdqc_params_qc_t *params); + +int dlopen_passwdqc(void) { + return dlopen_many_sym_or_warn( + &passwdqc_dl, "libpasswdqc.so.1", LOG_DEBUG, + DLSYM_ARG(passwdqc_params_reset), + DLSYM_ARG(passwdqc_params_load), + DLSYM_ARG(passwdqc_params_parse), + DLSYM_ARG(passwdqc_params_free), + DLSYM_ARG(passwdqc_check), + DLSYM_ARG(passwdqc_random)); +} + +static int pwqc_allocate_context(passwdqc_params_t **ret) { + + _cleanup_(sym_passwdqc_params_freep) passwdqc_params_t *params = NULL; + _cleanup_free_ char *load_reason = NULL; + int r; + + assert(ret); + + r = dlopen_passwdqc(); + if (r < 0) + return r; + + params = new0(passwdqc_params_t, 1); + if (!params) + return log_oom(); + + sym_passwdqc_params_reset(params); + + r = sym_passwdqc_params_load(params, &load_reason, "/etc/passwdqc.conf"); + if (r < 0) { + if (!load_reason) + return log_oom(); + log_debug("Failed to load passwdqc configuration file, ignoring: %s", load_reason); + } + + *ret = TAKE_PTR(params); + return 0; +} + +int suggest_passwords(void) { + + _cleanup_(sym_passwdqc_params_freep) passwdqc_params_t *params = NULL; + _cleanup_strv_free_erase_ char **suggestions = NULL; + _cleanup_(erase_and_freep) char *joined = NULL; + int r; + + r = pwqc_allocate_context(¶ms); + if (r < 0) { + if (ERRNO_IS_NOT_SUPPORTED(r)) + return 0; + return log_error_errno(r, "Failed to allocate libpasswdqc context: %m"); + } + + suggestions = new0(char*, N_SUGGESTIONS+1); + if (!suggestions) + return log_oom(); + + for (size_t i = 0; i < N_SUGGESTIONS; i++) { + suggestions[i] = sym_passwdqc_random(¶ms->qc); + if (!suggestions[i]) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate password, ignoring"); + } + + joined = strv_join(suggestions, " "); + if (!joined) + return log_oom(); + + printf("Password suggestions: %s\n", joined); + return 1; +} + +int check_password_quality( + const char *password, + const char *old, + const char *username, + char **ret_error) { + + _cleanup_(sym_passwdqc_params_freep) passwdqc_params_t *params = NULL; + const char *check_reason; + int r; + + assert(password); + + r = pwqc_allocate_context(¶ms); + if (r < 0) + return log_debug_errno(r, "Failed to allocate libpasswdqc context: %m"); + + if (username) { + const struct passwd pw = { + .pw_name = (char *) username, + /* + * passwdqc_check() could use this information to check + * whether the password is based on the personal login information, + * but we cannot provide it. + */ + .pw_passwd = (char *) "", + .pw_gecos = (char *) "", + .pw_dir = (char *) "", + .pw_shell = (char *) "" + }; + + check_reason = sym_passwdqc_check(¶ms->qc, password, old, &pw); + } else + check_reason = sym_passwdqc_check(¶ms->qc, password, old, /* pw */ NULL); + + if (check_reason) { + if (ret_error) { + char *e = strdup(check_reason); + if (!e) + return log_oom(); + *ret_error = e; + } + + return 0; /* all bad */ + } + + return 1; /* all good */ +} + +#endif diff --git a/src/shared/password-quality-util-passwdqc.h b/src/shared/password-quality-util-passwdqc.h new file mode 100644 index 0000000000..0d528d2089 --- /dev/null +++ b/src/shared/password-quality-util-passwdqc.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "macro.h" + +#if HAVE_PASSWDQC +#include + +extern void (*sym_passwdqc_params_reset)(passwdqc_params_t *params); +extern int (*sym_passwdqc_params_load)(passwdqc_params_t *params, char **reason, const char *pathname); +extern int (*sym_passwdqc_params_parse)(passwdqc_params_t *params, char **reason, int argc, const char *const *argv); +extern void (*sym_passwdqc_params_free)(passwdqc_params_t *params); +extern const char *(*sym_passwdqc_check)(const passwdqc_params_qc_t *params, const char *newpass, const char *oldpass, const struct passwd *pw); +extern char *(*sym_passwdqc_random)(const passwdqc_params_qc_t *params); + +int dlopen_passwdqc(void); + +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(passwdqc_params_t*, sym_passwdqc_params_free, NULL); + +int suggest_passwords(void); +int check_password_quality(const char *password, const char *old, const char *username, char **ret_error); + +#endif diff --git a/src/shared/password-quality-util.h b/src/shared/password-quality-util.h index a55727d1fb..f838ba73ef 100644 --- a/src/shared/password-quality-util.h +++ b/src/shared/password-quality-util.h @@ -3,7 +3,11 @@ #define N_SUGGESTIONS 6 -#if HAVE_PWQUALITY +#if HAVE_PASSWDQC + +#include "password-quality-util-passwdqc.h" + +#elif HAVE_PWQUALITY #include "password-quality-util-pwquality.h" diff --git a/src/test/test-dlopen-so.c b/src/test/test-dlopen-so.c index 2d3b7744f6..e98b8dae1e 100644 --- a/src/test/test-dlopen-so.c +++ b/src/test/test-dlopen-so.c @@ -10,6 +10,7 @@ #include "libfido2-util.h" #include "macro.h" #include "main-func.h" +#include "password-quality-util-passwdqc.h" #include "password-quality-util-pwquality.h" #include "pcre2-util.h" #include "pkcs11-util.h" @@ -32,6 +33,10 @@ static int run(int argc, char **argv) { assert_se(dlopen_cryptsetup() >= 0); #endif +#if HAVE_PASSWDQC + assert_se(dlopen_passwdqc() >= 0); +#endif + #if HAVE_PWQUALITY assert_se(dlopen_pwquality() >= 0); #endif