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 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <ctype.h>
#include <stdio.h>
#include "attr.h"
#include "prototypes.h"
#include "defines.h"
#include "getdef.h"
#include "string/memset/memzero.h"
#include "string/sprintf/xasprintf.h"
#include "string/strcmp/streq.h"
#include "string/strdup/xstrdup.h"
#if WITH_LIBBSD == 0
#include "freezero.h"
#endif /* WITH_LIBBSD */
/*
* can't be a palindrome - like `R A D A R' or `M A D A M'
*/
static bool palindrome (MAYBE_UNUSED const char *old, const char *new)
{
size_t i, j;
i = strlen (new);
for (j = 0; j < i; j++) {
if (new[i - j - 1] != new[j]) {
return false;
}
}
return true;
}
/*
* more than half of the characters are different ones.
*/
static bool similar (/*@notnull@*/const char *old, /*@notnull@*/const char *new)
{
int i, j;
/*
* XXX - sometimes this fails when changing from a simple password
* to a really long one (MD5). For now, I just return success if
* the new password is long enough. Please feel free to suggest
* something better... --marekm
*/
if (strlen (new) >= 8) {
return false;
}
for (i = j = 0; ('\0' != new[i]) && ('\0' != old[i]); i++) {
if (strchr (new, old[i]) != NULL) {
j++;
}
}
if (i >= j * 2) {
return false;
}
return true;
}
static char *str_lower (/*@returned@*/char *string)
{
char *cp;
for (cp = string; !streq(cp, ""); cp++) {
*cp = tolower (*cp);
}
return string;
}
static /*@observer@*//*@null@*/const char *password_check (
/*@notnull@*/const char *old,
/*@notnull@*/const char *new,
/*@notnull@*/MAYBE_UNUSED const struct passwd *pwdp)
{
const char *msg = NULL;
char *oldmono, *newmono, *wrapped;
if (streq(new, old)) {
return _("no change");
}
newmono = str_lower (xstrdup (new));
oldmono = str_lower (xstrdup (old));
xasprintf(&wrapped, "%s%s", oldmono, oldmono);
if (palindrome (oldmono, newmono)) {
msg = _("a palindrome");
} else if (streq(oldmono, newmono)) {
msg = _("case changes only");
} else if (similar (oldmono, newmono)) {
msg = _("too similar");
} else if (strstr (wrapped, newmono) != NULL) {
msg = _("rotated");
}
free(strzero(newmono));
free(strzero(oldmono));
free(strzero(wrapped));
return msg;
}
static /*@observer@*//*@null@*/const char *obscure_msg (
/*@notnull@*/const char *old,
/*@notnull@*/const char *new,
/*@notnull@*/const struct passwd *pwdp)
{
size_t maxlen, oldlen, newlen;
char *new1, *old1;
const char *msg;
const char *result;
oldlen = strlen (old);
newlen = strlen (new);
if (newlen < (size_t) getdef_num ("PASS_MIN_LEN", 0)) {
return _("too short");
}
/*
* Remaining checks are optional.
*/
if (!getdef_bool ("OBSCURE_CHECKS_ENAB")) {
return NULL;
}
msg = password_check (old, new, pwdp);
if (NULL != msg) {
return msg;
}
result = getdef_str ("ENCRYPT_METHOD");
if (NULL == result) {
/* The traditional crypt() truncates passwords to 8 chars. It is
possible to circumvent the above checks by choosing an easy
8-char password and adding some random characters to it...
Example: "password$%^&*123". So check it again, this time
truncated to the maximum length. Idea from npasswd. --marekm */
if (getdef_bool ("MD5_CRYPT_ENAB")) {
return NULL;
}
} else {
if ( streq(result, "MD5")
#ifdef USE_SHA_CRYPT
|| streq(result, "SHA256")
|| streq(result, "SHA512")
#endif
#ifdef USE_BCRYPT
|| streq(result, "BCRYPT")
#endif
#ifdef USE_YESCRYPT
|| streq(result, "YESCRYPT")
#endif
) {
return NULL;
}
}
maxlen = getdef_num ("PASS_MAX_LEN", 8);
if ( (oldlen <= maxlen)
&& (newlen <= maxlen)) {
return NULL;
}
new1 = xstrdup (new);
old1 = xstrdup (old);
if (newlen > maxlen)
stpcpy(&new1[maxlen], "");
if (oldlen > maxlen)
stpcpy(&old1[maxlen], "");
msg = password_check (old1, new1, pwdp);
freezero (new1, newlen);
freezero (old1, oldlen);
return msg;
}
/*
* Obscure - see if password is obscure enough.
*
* The programmer is encouraged to add as much complexity to this
* routine as desired. Included are some of my favorite ways to
* check passwords.
*/
bool obscure (const char *old, const char *new, const struct passwd *pwdp)
{
const char *msg = obscure_msg (old, new, pwdp);
if (NULL != msg) {
printf (_("Bad password: %s. "), msg);
return false;
}
return true;
}
|