From 1d8aae432724a98af82fcca7c02f80793a7e0731 Mon Sep 17 00:00:00 2001 From: Egor Ignatov Date: Fri, 6 Aug 2021 11:41:01 +0300 Subject: [PATCH] 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 fa2f7e35449..3b29807e93d 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 46a74b76358..19093238500 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 5c2909688d2..38f4acb0e79 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 fe88731c32f..8fe02410d63 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 00000000000..adfc14d7404 --- /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 00000000000..0d528d2089f --- /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 a55727d1fb5..f838ba73efe 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 2d3b7744f68..e98b8dae1e0 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 -- 2.47.3