Linux PAM
正在看linux系统密码验证的部分,现在的问题是ubuntu系统是也用linux-pam,还是用自己shadow-4.5 package自带的功能。
LFS (Linux From Scratch):
Shadow was indeed installed in LFS and there is no reason to reinstall it unless you installed CrackLib or Linux-PAM after your LFS system was completed. If you have installed CrackLib after LFS, then reinstalling Shadow will enable strong password support. If you have installed Linux-PAM, reinstalling Shadow will allow programs such as login and su to utilize PAM.
意思是说没有linux-pam,shadow包里的login什么的也能正常工作,但装了linux-pam就可以用更强壮的密码。
Historically an application that has required a given user to be authenticated, has had to be compiled to use a specific authentication mechanism. For example, in the case of traditional UN*X systems, the identity of the user is verified by the user entering a correct password. This password, after being prefixed by a two character ``salt’’, is encrypted (with crypt(3)). The user is then authenticated if this encrypted password is identical to the second field of the user’s entry in the system password database (the /etc/passwd file).
CRYPT(5) BSD File Formats Manual CRYPT(5)
NAME
crypt — storage format for hashed passphrases and available hashing methods
DESCRIPTION
The hashing methods implemented by crypt(3) are designed only to process user passphrases for storage and authentication; they are not suitable for use as general-purpose cryptographic hashes. Passphrase hashing is not a replacement for strong passphrases. It is always possible for an attacker with access to the hashed passphrases to guess and check possible cleartext passphrases. However, with a strong hashing method, guessing will be too slow for the attacker to discover a strong passphrase. All of the hashing methods use a “salt” to perturb the hash function, so that the same passphrase may produce many possible hashes. Newer methods accept longer salt strings. The salt should be chosen at random for each user. Salt defeats a number of attacks: 1. It is not possible to hash a passphrase once and then test it against each account's stored hash; the hash calculation must be repeated for each account. 2. It is not possible to tell whether two accounts use the same passphrase without successfully guessing one of the phrases. 3. Tables of precalculated hashes of commonly used passphrases must have an entry for each possible salt, which makes them impractically large. All of the hashing methods are also deliberately engineered to be slow; they use many iterations of an underlying cryptographic primitive to increase the cost of each guess. The newer hashing methods allow the number of iterations to be adjusted, using the “CPU time cost” parameter to crypt_gensalt(3). This makes it possible to keep the hash slow as hardware improves.
FORMAT OF HASHED PASSPHRASES
All of the hashing methods supported by crypt(3) produce a hashed passphrase which consists of four components: prefix, options, salt, and hash. The prefix controls which hashing method is to be used, and is the appropriate string to pass to crypt_gensalt(3) to select that method. The contents of options, salt, and hash are up to the method. Depending on the method, the prefix and options components may be empty. The setting argument to crypt(3) must begin with the first three components of a valid hashed passphrase, but anything after that is ignored. This makes authentication simple: hash the input passphrase using the stored passphrase as the setting, and then compare the result to the stored passphrase.
md5crypt A hash based on the MD5 algorithm, originally developed by Poul-Henning Kamp for FreeBSD. Supported on most free Unixes and newer versions of Solaris. Not as weak as the DES-based hashes below, but MD5 is so cheap on modern hard‐ ware that it should not be used for new hashes. CPU time cost is not adjustable.
Prefix "$1$" Hashed passphrase format \$1\$[^$]{1,8}\$[./0-9A-Za-z]{22} Maximum passphrase length unlimited Hash size 128 bits Salt size 6 to 48 bits CPU time cost parameter 1000
sha512crypt A hash based on SHA-2 with 512-bit output, originally developed by Ulrich Drepper for GNU libc. Supported on Linux but not common elsewhere. Ac‐ ceptable for new hashes. The default CPU time cost parameter is 5000, which is too low for modern hardware.
Prefix "$6$" Hashed passphrase format \$6\$(rounds=[1-9][0-9]+\$)?[./0-9A-Za-z]{1,16}\$[./0-9A-Za-z]{86} Maximum passphrase length unlimited Hash size 512 bits Salt size 6 to 96 bits CPU time cost parameter 1000 to 999,999,999
比如这个crypt(),函数原型是这样。
#include <crypt.h>
char *
crypt(const char *phrase, const char *setting);
The crypt, crypt_r, crypt_rn, and crypt_ra functions irreversibly “hash” phrase for storage in the system password database (shadow(5)) using a cryp‐ tographic “hashing method.” The result of this operation is called a “hashed passphrase” or just a “hash.” Hashing methods are described in crypt(5). setting controls which hashing method to use, and also supplies various parameters to the chosen method, most importantly a random “salt” which en‐ sures that no two stored hashes are the same, even if the phrase strings are the same.
这个函数的源码再glibc里。
glibc/crypt/crypt-entry.c
char *
crypt (const char *key, const char *salt)
{
#ifdef _LIBC
/* Try to find out whether we have to use MD5 encryption replacement. */
if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0
/* Let __crypt_r deal with the error code if FIPS is enabled. */
&& !fips_enabled_p ())
return __md5_crypt (key, salt);
/* Try to find out whether we have to use SHA256 encryption replacement. */
if (strncmp (sha256_salt_prefix, salt, sizeof (sha256_salt_prefix) - 1) == 0)
return __sha256_crypt (key, salt);
/* Try to find out whether we have to use SHA512 encryption replacement. */
if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0)
return __sha512_crypt (key, salt);
#endif
return __crypt_r (key, salt, &_ufc_foobar);
}
这个函数和linux-pam里的Goodcrypt_md5用法一样,都是:
pp = crypt("passwordinputed", "$1$somesalt$hashedstring");
返回的结果也是同样的形式,
pp = "$1$somesalt$justgeneratedhashstring"
然后比较原来的hash和新生成的pp。
linux-pam/modules/pam_unix/passverify.c
PAMH_ARG_DECL(int verify_pwd_hash,
const char *p, char *hash, unsigned int nullok)
{
...
/* the moment of truth -- do we agree with the password? */
D(("comparing state of pp[%s] and hash[%s]", pp, hash));
if (pp && strcmp(pp, hash) == 0) {
retval = PAM_SUCCESS;
} else {
retval = PAM_AUTH_ERR;
}
...
return retval;
}
shadow/contrib/pwdauth.c
/*
* Verify the password. The system-dependent shadow support is here.
*/
static int
password_auth_ok(pw, pass)
const struct passwd *pw;
const char *pass;
{
...
if (*pass || *cp)
result = (strcmp(crypt(pass, cp), cp) == 0);
else
result = 1; /* user with no password */
...
return result;
}
可以看出最终认证的方式都是strcmp。 并且hash都是8字节对齐的,这个和具体的hash方式有关。 对于md5来说,分割符号是$1$后面还有个$,加起来是4个字节。但也有这种$2b$。 salt从manual上看长度是个范围,但在ubuntu上,可能是8字节或16字节。
整个字串基本能确定是4字节对齐,但不一定是8字节对齐。所以在64位机器上,需要具体看strcmp的实现。