/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #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_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) { return dlopen_many_sym_or_warn( &pwquality_dl, "libpwquality.so.1", LOG_DEBUG, DLSYM_ARG(pwquality_check), DLSYM_ARG(pwquality_default_settings), DLSYM_ARG(pwquality_free_settings), DLSYM_ARG(pwquality_generate), DLSYM_ARG(pwquality_get_str_value), DLSYM_ARG(pwquality_read_config), DLSYM_ARG(pwquality_set_int_value), DLSYM_ARG(pwquality_strerror)); } static 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; } 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)); } 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; 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; } 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 (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) 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(); 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_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL; char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; void *auxerror; int r; assert(password); r = pwq_allocate_context(&pwq); if (r < 0) return log_debug_errno(r, "Failed to allocate libpwquality context: %m"); r = sym_pwquality_check(pwq, password, old, username, &auxerror); if (r < 0) { if (ret_error) { _cleanup_free_ char *e = NULL; e = strdup(sym_pwquality_strerror(buf, sizeof(buf), r, auxerror)); if (!e) return -ENOMEM; *ret_error = TAKE_PTR(e); } return 0; /* all bad */ } return 1; /* all good */ } #endif