summaryrefslogtreecommitdiffstats
path: root/src/shared/password-quality-util-pwquality.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/password-quality-util-pwquality.c')
-rw-r--r--src/shared/password-quality-util-pwquality.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/src/shared/password-quality-util-pwquality.c b/src/shared/password-quality-util-pwquality.c
new file mode 100644
index 0000000..80f7d58
--- /dev/null
+++ b/src/shared/password-quality-util-pwquality.c
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#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