summaryrefslogtreecommitdiffstats
path: root/panels/user-accounts/pw-utils.c
blob: 0f4dfd8b05d8054d8f5d6be5a6f3212b926ce2f2 (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
165
166
167
168
169
170
171
172
173
174
175
176
177
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright 2012  Red Hat, Inc,
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 * Written by: Matthias Clasen <mclasen@redhat.com>
 */

#include "config.h"

#include "pw-utils.h"

#include <glib.h>
#include <glib/gi18n.h>

#include <pwquality.h>

static pwquality_settings_t *
get_pwq (void)
{
        static pwquality_settings_t *settings;

        if (settings == NULL) {
                gchar *err = NULL;
                gint rv = 0;

                settings = pwquality_default_settings ();
                pwquality_set_int_value (settings, PWQ_SETTING_MAX_SEQUENCE, 4);

                rv = pwquality_read_config (settings, NULL, (gpointer)&err);
                if (rv < 0) {
                        g_warning ("failed to read pwquality configuration: %s\n",
                                   pwquality_strerror (NULL, 0, rv, err));
                        pwquality_free_settings (settings);

                        /* Load just default settings in case of failure. */
                        settings = pwquality_default_settings ();
                        pwquality_set_int_value (settings, PWQ_SETTING_MAX_SEQUENCE, 4);
                }
        }

        return settings;
}

gint
pw_min_length (void)
{
        gint value = 0;
        gint rv;

        rv = pwquality_get_int_value (get_pwq (), PWQ_SETTING_MIN_LENGTH, &value);
        if (rv < 0) {
                g_warning ("Failed to read pwquality setting: %s\n",
                           pwquality_strerror (NULL, 0, rv, NULL));
        }

        return value;
}

gchar *
pw_generate (void)
{
        gchar *res;
        gint rv;

        rv = pwquality_generate (get_pwq (), 0, &res);

        if (rv < 0) {
                g_warning ("Password generation failed: %s\n",
                           pwquality_strerror (NULL, 0, rv, NULL));
                return NULL;
        }

        return res;
}

static const gchar *
pw_error_hint (gint error)
{
        switch (error) {
        case PWQ_ERROR_SAME_PASSWORD:
                return C_("Password hint", "The new password needs to be different from the old one.");
        case PWQ_ERROR_CASE_CHANGES_ONLY:
                return C_("Password hint", "Try changing some letters and numbers.");
        case PWQ_ERROR_TOO_SIMILAR:
                return C_("Password hint", "Try changing the password a bit more.");
        case PWQ_ERROR_USER_CHECK:
                return C_("Password hint", "A password without your user name would be stronger.");
        case PWQ_ERROR_GECOS_CHECK:
                return C_("Password hint", "Try to avoid using your name in the password.");
        case PWQ_ERROR_BAD_WORDS:
                return C_("Password hint", "Try to avoid some of the words included in the password.");
        case PWQ_ERROR_ROTATED:
                return C_("Password hint", "Try changing the password a bit more.");
        case PWQ_ERROR_CRACKLIB_CHECK:
                return C_("Password hint", "Try to avoid common words.");
        case PWQ_ERROR_PALINDROME:
                return C_("Password hint", "Try to avoid reordering existing words.");
        case PWQ_ERROR_MIN_DIGITS:
                return C_("Password hint", "Try to use more numbers.");
        case PWQ_ERROR_MIN_UPPERS:
                return C_("Password hint", "Try to use more uppercase letters.");
        case PWQ_ERROR_MIN_LOWERS:
                return C_("Password hint", "Try to use more lowercase letters.");
        case PWQ_ERROR_MIN_OTHERS:
                return C_("Password hint", "Try to use more special characters, like punctuation.");
        case PWQ_ERROR_MIN_CLASSES:
                return C_("Password hint", "Try to use a mixture of letters, numbers and punctuation.");
        case PWQ_ERROR_MAX_CONSECUTIVE:
                return C_("Password hint", "Try to avoid repeating the same character.");
        case PWQ_ERROR_MAX_CLASS_REPEAT:
                return C_("Password hint", "Try to avoid repeating the same type of character: you need to mix up letters, numbers and punctuation.");
        case PWQ_ERROR_MAX_SEQUENCE:
                return C_("Password hint", "Try to avoid sequences like 1234 or abcd.");
        case PWQ_ERROR_MIN_LENGTH:
                return C_("Password hint", "Password needs to be longer. Try to add more letters, numbers and punctuation.");
        case PWQ_ERROR_EMPTY_PASSWORD:
                return C_("Password hint", "Mix uppercase and lowercase and try to use a number or two.");
        default:
                return C_("Password hint", "Adding more letters, numbers and punctuation will make the password stronger.");
        }
}

gdouble
pw_strength (const gchar  *password,
             const gchar  *old_password,
             const gchar  *username,
             const gchar **hint,
             gint         *strength_level)
{
        gint rv, level, length = 0;
        gdouble strength = 0.0;
        void *auxerror;

        rv = pwquality_check (get_pwq (),
                              password, old_password, username,
                              &auxerror);

        if (password != NULL)
                length = strlen (password);

        strength = CLAMP (0.01 * rv, 0.0, 1.0);
        if (rv < 0) {
                level = (length > 0) ? 1 : 0;
        }
        else if (strength < 0.50) {
                level = 2;
        } else if (strength < 0.75) {
                level = 3;
        } else if (strength < 0.90) {
                level = 4;
        } else {
                level = 5;
        }

        if (length && length < pw_min_length())
                *hint = pw_error_hint (PWQ_ERROR_MIN_LENGTH);
        else
                *hint = pw_error_hint (rv);

        if (strength_level)
                *strength_level = level;

        return strength;
}