summaryrefslogtreecommitdiffstats
path: root/src/shared/password-quality-util-pwquality.c
blob: 7456469c86e8025c271903773779a0638c237fad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* 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;

DLSYM_FUNCTION(pwquality_check);
DLSYM_FUNCTION(pwquality_default_settings);
DLSYM_FUNCTION(pwquality_free_settings);
DLSYM_FUNCTION(pwquality_generate);
DLSYM_FUNCTION(pwquality_get_str_value);
DLSYM_FUNCTION(pwquality_read_config);
DLSYM_FUNCTION(pwquality_set_int_value);
DLSYM_FUNCTION(pwquality_strerror);

int dlopen_pwquality(void) {
        ELF_NOTE_DLOPEN("pwquality",
                        "Support for password quality checks",
                        ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
                        "libpwquality.so.1");

        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];
        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 (size_t 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) {
                        r = strdup_to(ret_error,
                                      sym_pwquality_strerror(buf, sizeof(buf), r, auxerror));
                        if (r < 0)
                                return r;
                }

                return 0; /* all bad */
        }

        return 1; /* all good */
}

#endif