At least in my Ubuntu, the "too similar" messages cames out when: "...more than half of the characters are different ones...." (see below for details). thanks to the PAM support, as clearly explained in the @slhck answer.
For other platform, where PAM is not used, the "too similar" messages comes out when: "...more than half of the characters are different ones...." (see below for details)
To further check this statement on your own, it's possible to check the source-code. Here is how.
The "passwd" program is included in the passwd package:
verzulli@iMac:~$ which passwd
/usr/bin/passwd
verzulli@iMac:~$ dpkg -S /usr/bin/passwd
passwd: /usr/bin/passwd
As we're dealing with Open Source technologies, we have unrestricted access to source code. Getting it is as simple as:
verzulli@iMac:/usr/local/src/passwd$ apt-get source passwd
Afterwards it's easy to find the relevant fragment of code:
verzulli@iMac:/usr/local/src/passwd$ grep -i -r 'too similar' .
[...]
./shadow-4.1.5.1/NEWS:- new password is not "too similar" if it is long enough
./shadow-4.1.5.1/libmisc/obscure.c: msg = _("too similar");
A quick check to the "obscure.c" gives out this (I'm cut-and-pasting only the relevant piece of code):
static const char *password_check (
const char *old,
const char *new,
const struct passwd *pwdp)
{
const char *msg = NULL;
char *oldmono, *newmono, *wrapped;
if (strcmp (new, old) == 0) {
return _("no change");
}
[...]
if (palindrome (oldmono, newmono)) {
msg = _("a palindrome");
} else if (strcmp (oldmono, newmono) == 0) {
msg = _("case changes only");
} else if (similar (oldmono, newmono)) {
msg = _("too similar");
} else if (simple (old, new)) {
msg = _("too simple");
} else if (strstr (wrapped, newmono) != NULL) {
msg = _("rotated");
} else {
}
[...]
return msg;
}
So, now, we know that there's a "similar" function that based on the old-one and the new-one check if both are similar. Here's the snippet:
/*
* more than half of the characters are different ones.
*/
static bool similar (const char *old, 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;
}
I haven't reviewed the C code. I limited myself in trusting the comment just before the function definition :-)
The differentiation between PAM and NON-PAM aware platforms is defined in the "obscure.c" file that is structured like:
#include <config.h>
#ifndef USE_PAM
[...lots of things, including all the above...]
#else /* !USE_PAM */
extern int errno; /* warning: ANSI C forbids an empty source file */
#endif /* !USE_PAM */
In windows, it keeps a history of past passwords you have used, i didn't know Linux did this too. But hash would probably be the
/etc/user
file that you're thinking of. Not the password history – xR34P3Rx – 2014-12-27T17:33:04.053301st off: plain text? no. If(!) saved you save the hash and compare hashes. In Linux though it checks current password with new password. BOTH are supplied by the user when changing passwords. – Rinzwind – 2014-12-27T17:43:00.660
42@Rinzwind But comparing hashes won't work because a one character difference should result in a completely different hash – slhck – 2014-12-27T18:01:59.253
17
See also Does Facebook store plain-text passwords? on [security.se] for other ways to detect similarity given only the hash of the old password and plaintext of the new password (no plaintext for old).
– Bob – 2014-12-28T06:56:17.11721You can actually test for similarity between a hashed old password and a plaintext new password. Simply generate a list of passwords similar to the new one, hash them all, and compare the resulting hashes to the old password hash. If any match, then it's similar. – BWG – 2014-12-29T07:11:10.117
2@BWG: That is a slight oversimplification – current hashing schemes salt the hash, so first you havce to extract the salt from the old password hash and make sure you use that salt for your similar-to-new passwords. (I'm pointing this out because it's possible that the API wouldn't expose a way to force a specific salt.) – Ulrich Schwarz – 2015-01-01T14:07:40.747
1@UlrichSchwarz: There is generally a way to force checking with the right salt - otherwise it's very difficult to verify the user's password at login, for example ;-) – psmears – 2015-01-01T18:53:58.987
1@psmears: but checking with the right salt is not at all creating with a given salt. Creating turns a password into an opaque string, nothing outside the API knows anything about it, not even if salt is there, it just knows that if you hand that string and a password to the API again, you get
true
exactly if you have the password right. – Ulrich Schwarz – 2015-01-01T19:11:45.3631@UlrichSchwarz:In practice the whole operation is exactly the same: checking whether a string (that happens to be a variant of a proposed new password, for "similarity" checking) matches the stored hash+salt is exactly the same operation as checking whether a string (that happens to be the password the user gave at login) matches the stored hash+salt. Yes, you might have to do the "generate hash with appropriate salt and compare" parts all inside the API, rather than having the API spit out the hashes, but it's not going to be the case (in any sensible API) that the API doesn't allow it :) – psmears – 2015-01-01T19:42:01.090