How does adduser generate passwords?

Associate
Joined
24 Jun 2007
Posts
1,869
Location
Landan.
Afternoon all.

I ran into an interesting problem the other day whilst playing around with John the Ripper. It would seem Ubuntu 8.10 now uses SHA512 to generate passwords, and as such JtR can't crack them (or at least attempt to).

This got me on to coding my own password hasher in Python and C Sharp (.NET :o - I have to learn it at some point tho!) - but both are giving different results than the hash stored in /etc/shadow.

I'm generating the passwords by prefixing the word with the salt (the six chars contained between the second and third $ signs, and then SHA512'ing the result.

Still to no avail - can anyone give me a breakdown of the password generating process that adduser takes?

I've had a look through adduser's source code but can't seem to find anything (i.e. no calls to cryptlib()).

Thanks :)
 
I know, I know :o - but I need to learn both the languages and C doesn't appeal. Crypto's something that interests me so I thought it'd be a good project for the weekend :)

Just had a look through the passwd source (I feel even more noobish for thinking that adduser did it itself :o) - but I can't see anything, any pointers?
 
These passwords are generated with PAM (pluggable authentication module), I've just had a look through the source, and as far as I can tell the SHA512 hash is generated by the call call: crypt(password, salt) from the pam_unix module, I haven't looked much deeper than this, but im pretty sure this function comes down to something in libcrypt or maybe even in glibc. There is some more stuff to it though than just concatenating the salt with the password and SHA'ing it.

Look up 'man 3 crypt' for the function : char *crypt(const char *key, const char *salt);

If salt is a character string starting with the characters "$id$" fol-
lowed by a string terminated by "$":

$id$salt$encrypted

then instead of using the DES machine, id identifies the encryption
method used and this then determines how the rest of the password
string is interpreted. The following values of id are supported:

ID | Method
---------------------------------------------------------
1 | MD5
2a | Blowfish (not in mainline glibc; added in some
| Linux distributions)
5 | SHA-256 (since glibc 2.7)
6 | SHA-512 (since glibc 2.7)

Thats how PAM sets it up, with 6 being the algoid in the pam_unix c source code. So yer, look at the SHA implementation in the crypt(3) function if you wanna see the internals.

Heres the relevant create_password_hash function from PAM:

Code:
char *
create_password_hash(const char *password, unsigned int ctrl, int rounds)
{
	const char *algoid;
	char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */
	char *sp;

	if (on(UNIX_MD5_PASS, ctrl)) {
		return crypt_md5_wrapper(password);
	}
	if (on(UNIX_SHA256_PASS, ctrl)) {
		algoid = "$5$";
	} else if (on(UNIX_SHA512_PASS, ctrl)) {
		algoid = "$6$";
	} else { /* must be crypt/bigcrypt */
		char tmppass[9];
		char *crypted;

		crypt_make_salt(salt, 2);
		if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) {
			strncpy(tmppass, password, sizeof(tmppass)-1);
			tmppass[sizeof(tmppass)-1] = '\0';
			password = tmppass;
		}
		crypted = bigcrypt(password, salt);
		memset(tmppass, '\0', sizeof(tmppass));
		password = NULL;
		return crypted;
	}

	sp = stpcpy(salt, algoid);
	if (on(UNIX_ALGO_ROUNDS, ctrl)) {
		sp += snprintf(sp, sizeof(salt) - 3, "rounds=%u$", rounds);
	}
	crypt_make_salt(sp, 8);
	/* For now be conservative so the resulting hashes
	 * are not too long. 8 bytes of salt prevents dictionary
	 * attacks well enough. */
	sp = crypt(password, salt);
	if (strncmp(algoid, sp, strlen(algoid)) != 0) {
	/* libc doesn't know the algorithm, use MD5 */
		memset(sp, '\0', strlen(sp));
		return crypt_md5_wrapper(password);
	}

	return x_strdup(sp);
}

Just learn how crypt() works and your sorted.
 
Last edited:
These passwords are generated with PAM (pluggable authentication module), I've just had a look through the source, and as far as I can tell the SHA512 hash is generated by the call call: crypt(password, salt) from the pam_unix module, I haven't looked much deeper than this, but im pretty sure this function comes down to something in libcrypt or maybe even in glibc. There is some more stuff to it though than just concatenating the salt with the password and SHA'ing it.

Look up 'man 3 crypt' for the function : char *crypt(const char *key, const char *salt);



Thats how PAM sets it up, with 6 being the algoid in the pam_unix c source code. So yer, look at the SHA implementation in the crypt(3) function if you wanna see the internals.

Heres the relevant create_password_hash function from PAM:

Code:
char *
create_password_hash(const char *password, unsigned int ctrl, int rounds)
{
    const char *algoid;
    char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */
    char *sp;

    if (on(UNIX_MD5_PASS, ctrl)) {
        return crypt_md5_wrapper(password);
    }
    if (on(UNIX_SHA256_PASS, ctrl)) {
        algoid = "$5$";
    } else if (on(UNIX_SHA512_PASS, ctrl)) {
        algoid = "$6$";
    } else { /* must be crypt/bigcrypt */
        char tmppass[9];
        char *crypted;

        crypt_make_salt(salt, 2);
        if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) {
            strncpy(tmppass, password, sizeof(tmppass)-1);
            tmppass[sizeof(tmppass)-1] = '\0';
            password = tmppass;
        }
        crypted = bigcrypt(password, salt);
        memset(tmppass, '\0', sizeof(tmppass));
        password = NULL;
        return crypted;
    }

    sp = stpcpy(salt, algoid);
    if (on(UNIX_ALGO_ROUNDS, ctrl)) {
        sp += snprintf(sp, sizeof(salt) - 3, "rounds=%u$", rounds);
    }
    crypt_make_salt(sp, 8);
    /* For now be conservative so the resulting hashes
     * are not too long. 8 bytes of salt prevents dictionary
     * attacks well enough. */
    sp = crypt(password, salt);
    if (strncmp(algoid, sp, strlen(algoid)) != 0) {
    /* libc doesn't know the algorithm, use MD5 */
        memset(sp, '\0', strlen(sp));
        return crypt_md5_wrapper(password);
    }

    return x_strdup(sp);
}
Just learn how crypt() works and your sorted.

Thanks for this, was exactly what I needed :)

Just looking at crypt() now.
 
Back
Top Bottom