mirror of
https://github.com/php/php-src.git
synced 2024-09-22 02:17:32 +00:00
Refactor password.c
Use zend_string instread of char*/size_t Clean up use of scope vars Get rid of some temporaries/overstacked expressions.
This commit is contained in:
parent
3de82a0349
commit
7165e28738
@ -58,28 +58,30 @@ PHP_MINIT_FUNCTION(password) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static char* php_password_get_algo_name(const php_password_algo algo)
|
||||
static zend_string* php_password_get_algo_name(const php_password_algo algo)
|
||||
{
|
||||
switch (algo) {
|
||||
case PHP_PASSWORD_BCRYPT:
|
||||
return "bcrypt";
|
||||
return zend_string_init("bcrypt", sizeof("bcrypt") - 1, 0);
|
||||
#if HAVE_ARGON2LIB
|
||||
case PHP_PASSWORD_ARGON2I:
|
||||
return "argon2i";
|
||||
return zend_string_init("argon2i", sizeof("argon2i") - 1, 0);
|
||||
#endif
|
||||
case PHP_PASSWORD_UNKNOWN:
|
||||
default:
|
||||
return "unknown";
|
||||
return zend_string_init("unknown", sizeof("unknown") - 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static php_password_algo php_password_determine_algo(const char *hash, const size_t len)
|
||||
static php_password_algo php_password_determine_algo(const zend_string *hash)
|
||||
{
|
||||
if (len > 3 && hash[0] == '$' && hash[1] == '2' && hash[2] == 'y' && len == 60) {
|
||||
const char *h = ZSTR_VAL(hash);
|
||||
const size_t len = ZSTR_LEN(hash);
|
||||
if (len == 60 && h[0] == '$' && h[1] == '2' && h[2] == 'y') {
|
||||
return PHP_PASSWORD_BCRYPT;
|
||||
}
|
||||
#if HAVE_ARGON2LIB
|
||||
if (len >= sizeof("$argon2i$")-1 && !memcmp(hash, "$argon2i$", sizeof("$argon2i$")-1)) {
|
||||
if (len >= sizeof("$argon2i$")-1 && !memcmp(h, "$argon2i$", sizeof("$argon2i$")-1)) {
|
||||
return PHP_PASSWORD_ARGON2I;
|
||||
}
|
||||
#endif
|
||||
@ -128,39 +130,32 @@ static int php_password_salt_to64(const char *str, const size_t str_len, const s
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int php_password_make_salt(size_t length, char *ret) /* {{{ */
|
||||
static zend_string* php_password_make_salt(size_t length) /* {{{ */
|
||||
{
|
||||
size_t raw_length;
|
||||
char *buffer;
|
||||
char *result;
|
||||
zend_string *ret, *buffer;
|
||||
|
||||
if (length > (INT_MAX / 3)) {
|
||||
php_error_docref(NULL, E_WARNING, "Length is too large to safely generate");
|
||||
return FAILURE;
|
||||
php_error(E_WARNING, "Length is too large to safely generate");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
raw_length = length * 3 / 4 + 1;
|
||||
|
||||
buffer = (char *) safe_emalloc(raw_length, 1, 1);
|
||||
|
||||
if (FAILURE == php_random_bytes_silent(buffer, raw_length)) {
|
||||
php_error_docref(NULL, E_WARNING, "Unable to generate salt");
|
||||
efree(buffer);
|
||||
return FAILURE;
|
||||
buffer = zend_string_alloc(length * 3 / 4 + 1, 0);
|
||||
if (FAILURE == php_random_bytes_silent(ZSTR_VAL(buffer), ZSTR_LEN(buffer))) {
|
||||
php_error(E_WARNING, "Unable to generate salt");
|
||||
zend_string_release(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = safe_emalloc(length, 1, 1);
|
||||
if (php_password_salt_to64(buffer, raw_length, length, result) == FAILURE) {
|
||||
php_error_docref(NULL, E_WARNING, "Generated salt too short");
|
||||
efree(buffer);
|
||||
efree(result);
|
||||
return FAILURE;
|
||||
ret = zend_string_alloc(length, 0);
|
||||
if (php_password_salt_to64(ZSTR_VAL(buffer), ZSTR_LEN(buffer), length, ZSTR_VAL(ret)) == FAILURE) {
|
||||
php_error(E_WARNING, "Generated salt too short");
|
||||
zend_string_release(buffer);
|
||||
zend_string_release(ret);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(ret, result, length);
|
||||
efree(result);
|
||||
efree(buffer);
|
||||
ret[length] = 0;
|
||||
return SUCCESS;
|
||||
zend_string_release(buffer);
|
||||
ZSTR_VAL(ret)[length] = 0;
|
||||
return ret;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -169,24 +164,23 @@ Retrieves information about a given hash */
|
||||
PHP_FUNCTION(password_get_info)
|
||||
{
|
||||
php_password_algo algo;
|
||||
size_t hash_len;
|
||||
char *hash, *algo_name;
|
||||
zend_string *hash, *algo_name;
|
||||
zval options;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_STRING(hash, hash_len)
|
||||
Z_PARAM_STR(hash)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
array_init(&options);
|
||||
|
||||
algo = php_password_determine_algo(hash, (size_t) hash_len);
|
||||
algo = php_password_determine_algo(hash);
|
||||
algo_name = php_password_get_algo_name(algo);
|
||||
|
||||
switch (algo) {
|
||||
case PHP_PASSWORD_BCRYPT:
|
||||
{
|
||||
zend_long cost = PHP_PASSWORD_BCRYPT_COST;
|
||||
sscanf(hash, "$2y$" ZEND_LONG_FMT "$", &cost);
|
||||
sscanf(ZSTR_VAL(hash), "$2y$" ZEND_LONG_FMT "$", &cost);
|
||||
add_assoc_long(&options, "cost", cost);
|
||||
}
|
||||
break;
|
||||
@ -198,7 +192,7 @@ PHP_FUNCTION(password_get_info)
|
||||
zend_long time_cost = PHP_PASSWORD_ARGON2_TIME_COST;
|
||||
zend_long threads = PHP_PASSWORD_ARGON2_THREADS;
|
||||
|
||||
sscanf(hash, "$%*[argon2i]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, &v, &memory_cost, &time_cost, &threads);
|
||||
sscanf(ZSTR_VAL(hash), "$%*[argon2i]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, &v, &memory_cost, &time_cost, &threads);
|
||||
add_assoc_long(&options, "memory_cost", memory_cost);
|
||||
add_assoc_long(&options, "time_cost", time_cost);
|
||||
add_assoc_long(&options, "threads", threads);
|
||||
@ -213,7 +207,7 @@ PHP_FUNCTION(password_get_info)
|
||||
array_init(return_value);
|
||||
|
||||
add_assoc_long(return_value, "algo", algo);
|
||||
add_assoc_string(return_value, "algoName", algo_name);
|
||||
add_assoc_str(return_value, "algoName", algo_name);
|
||||
add_assoc_zval(return_value, "options", &options);
|
||||
}
|
||||
/** }}} */
|
||||
@ -224,19 +218,18 @@ PHP_FUNCTION(password_needs_rehash)
|
||||
{
|
||||
zend_long new_algo = 0;
|
||||
php_password_algo algo;
|
||||
size_t hash_len;
|
||||
char *hash;
|
||||
zend_string *hash;
|
||||
HashTable *options = 0;
|
||||
zval *option_buffer;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(2, 3)
|
||||
Z_PARAM_STRING(hash, hash_len)
|
||||
Z_PARAM_STR(hash)
|
||||
Z_PARAM_LONG(new_algo)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_ARRAY_OR_OBJECT_HT(options)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
algo = php_password_determine_algo(hash, (size_t) hash_len);
|
||||
algo = php_password_determine_algo(hash);
|
||||
|
||||
if ((zend_long)algo != new_algo) {
|
||||
RETURN_TRUE;
|
||||
@ -251,7 +244,7 @@ PHP_FUNCTION(password_needs_rehash)
|
||||
new_cost = zval_get_long(option_buffer);
|
||||
}
|
||||
|
||||
sscanf(hash, "$2y$" ZEND_LONG_FMT "$", &cost);
|
||||
sscanf(ZSTR_VAL(hash), "$2y$" ZEND_LONG_FMT "$", &cost);
|
||||
if (cost != new_cost) {
|
||||
RETURN_TRUE;
|
||||
}
|
||||
@ -277,7 +270,7 @@ PHP_FUNCTION(password_needs_rehash)
|
||||
new_threads = zval_get_long(option_buffer);
|
||||
}
|
||||
|
||||
sscanf(hash, "$%*[argon2i]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, &v, &memory_cost, &time_cost, &threads);
|
||||
sscanf(ZSTR_VAL(hash), "$%*[argon2i]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, &v, &memory_cost, &time_cost, &threads);
|
||||
|
||||
if (new_time_cost != time_cost || new_memory_cost != memory_cost || new_threads != threads) {
|
||||
RETURN_TRUE;
|
||||
@ -297,44 +290,35 @@ PHP_FUNCTION(password_needs_rehash)
|
||||
Verify a hash created using crypt() or password_hash() */
|
||||
PHP_FUNCTION(password_verify)
|
||||
{
|
||||
int status = 0;
|
||||
size_t i, password_len, hash_len;
|
||||
char *password, *hash;
|
||||
zend_string *ret;
|
||||
zend_string *password, *hash;
|
||||
php_password_algo algo;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(2, 2)
|
||||
Z_PARAM_STRING(password, password_len)
|
||||
Z_PARAM_STRING(hash, hash_len)
|
||||
Z_PARAM_STR(password)
|
||||
Z_PARAM_STR(hash)
|
||||
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
|
||||
|
||||
algo = php_password_determine_algo(hash, (size_t) hash_len);
|
||||
algo = php_password_determine_algo(hash);
|
||||
|
||||
switch(algo) {
|
||||
#if HAVE_ARGON2LIB
|
||||
case PHP_PASSWORD_ARGON2I:
|
||||
{
|
||||
argon2_type type = Argon2_i;
|
||||
|
||||
status = argon2_verify(hash, password, password_len, type);
|
||||
|
||||
if (status == ARGON2_OK) {
|
||||
RETURN_TRUE;
|
||||
}
|
||||
|
||||
RETURN_FALSE;
|
||||
}
|
||||
RETURN_BOOL(ARGON2_OK == argon2_verify(ZSTR_VAL(hash), ZSTR_VAL(password), ZSTR_LEN(password), Argon2_i));
|
||||
break;
|
||||
#endif
|
||||
case PHP_PASSWORD_BCRYPT:
|
||||
case PHP_PASSWORD_UNKNOWN:
|
||||
default:
|
||||
{
|
||||
if ((ret = php_crypt(password, (int)password_len, hash, (int)hash_len, 1)) == NULL) {
|
||||
size_t i;
|
||||
int status = 0;
|
||||
zend_string *ret = php_crypt(ZSTR_VAL(password), (int)ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
|
||||
|
||||
if (!ret) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (ZSTR_LEN(ret) != hash_len || hash_len < 13) {
|
||||
if (ZSTR_LEN(ret) != ZSTR_LEN(hash) || ZSTR_LEN(hash) < 13) {
|
||||
zend_string_free(ret);
|
||||
RETURN_FALSE;
|
||||
}
|
||||
@ -343,8 +327,8 @@ PHP_FUNCTION(password_verify)
|
||||
* resistance towards timing attacks. This is a constant time
|
||||
* equality check that will always check every byte of both
|
||||
* values. */
|
||||
for (i = 0; i < hash_len; i++) {
|
||||
status |= (ZSTR_VAL(ret)[i] ^ hash[i]);
|
||||
for (i = 0; i < ZSTR_LEN(hash); i++) {
|
||||
status |= (ZSTR_VAL(ret)[i] ^ ZSTR_VAL(hash)[i]);
|
||||
}
|
||||
|
||||
zend_string_free(ret);
|
||||
@ -361,11 +345,10 @@ PHP_FUNCTION(password_verify)
|
||||
Hash a password */
|
||||
PHP_FUNCTION(password_hash)
|
||||
{
|
||||
char hash_format[8], *hash, *salt, *password;
|
||||
char hash_format[8];
|
||||
zend_long algo = 0;
|
||||
size_t password_len = 0;
|
||||
int hash_len;
|
||||
size_t salt_len = 0, required_salt_len = 0, hash_format_len;
|
||||
zend_string *password, *salt;
|
||||
size_t required_salt_len = 0, hash_format_len;
|
||||
HashTable *options = 0;
|
||||
zval *option_buffer;
|
||||
|
||||
@ -377,7 +360,7 @@ PHP_FUNCTION(password_hash)
|
||||
#endif
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(2, 3)
|
||||
Z_PARAM_STRING(password, password_len)
|
||||
Z_PARAM_STR(password)
|
||||
Z_PARAM_LONG(algo)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_ARRAY_OR_OBJECT_HT(options)
|
||||
@ -477,51 +460,45 @@ PHP_FUNCTION(password_hash)
|
||||
zend_string_release(buffer);
|
||||
RETURN_NULL();
|
||||
} else if (php_password_salt_is_alphabet(ZSTR_VAL(buffer), ZSTR_LEN(buffer)) == FAILURE) {
|
||||
salt = safe_emalloc(required_salt_len, 1, 1);
|
||||
if (php_password_salt_to64(ZSTR_VAL(buffer), ZSTR_LEN(buffer), required_salt_len, salt) == FAILURE) {
|
||||
efree(salt);
|
||||
salt = zend_string_alloc(required_salt_len, 0);
|
||||
if (php_password_salt_to64(ZSTR_VAL(buffer), ZSTR_LEN(buffer), required_salt_len, ZSTR_VAL(salt)) == FAILURE) {
|
||||
zend_string_release(salt);
|
||||
php_error_docref(NULL, E_WARNING, "Provided salt is too short: %zd", ZSTR_LEN(buffer));
|
||||
zend_string_release(buffer);
|
||||
RETURN_NULL();
|
||||
}
|
||||
salt_len = required_salt_len;
|
||||
} else {
|
||||
salt = safe_emalloc(required_salt_len, 1, 1);
|
||||
memcpy(salt, ZSTR_VAL(buffer), required_salt_len);
|
||||
salt_len = required_salt_len;
|
||||
salt = zend_string_alloc(required_salt_len, 0);
|
||||
memcpy(ZSTR_VAL(salt), ZSTR_VAL(buffer), required_salt_len);
|
||||
}
|
||||
zend_string_release(buffer);
|
||||
} else {
|
||||
salt = safe_emalloc(required_salt_len, 1, 1);
|
||||
if (php_password_make_salt(required_salt_len, salt) == FAILURE) {
|
||||
efree(salt);
|
||||
salt = php_password_make_salt(required_salt_len);
|
||||
if (!salt) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
salt_len = required_salt_len;
|
||||
}
|
||||
|
||||
switch (algo) {
|
||||
case PHP_PASSWORD_BCRYPT:
|
||||
{
|
||||
zend_string *result;
|
||||
salt[salt_len] = 0;
|
||||
zend_string *result, *hash;
|
||||
ZSTR_VAL(salt)[ZSTR_LEN(salt)] = 0;
|
||||
|
||||
hash = safe_emalloc(salt_len + hash_format_len, 1, 1);
|
||||
sprintf(hash, "%s%s", hash_format, salt);
|
||||
hash[hash_format_len + salt_len] = 0;
|
||||
hash = zend_string_alloc(ZSTR_LEN(salt) + hash_format_len, 0);
|
||||
sprintf(ZSTR_VAL(hash), "%s%s", hash_format, ZSTR_VAL(salt));
|
||||
ZSTR_VAL(hash)[hash_format_len + ZSTR_LEN(salt)] = 0;
|
||||
|
||||
efree(salt);
|
||||
zend_string_release(salt);
|
||||
|
||||
/* This cast is safe, since both values are defined here in code and cannot overflow */
|
||||
hash_len = (int) (hash_format_len + salt_len);
|
||||
result = php_crypt(ZSTR_VAL(password), (int)ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
|
||||
zend_string_release(hash);
|
||||
|
||||
if ((result = php_crypt(password, (int)password_len, hash, hash_len, 1)) == NULL) {
|
||||
efree(hash);
|
||||
if (!result) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
efree(hash);
|
||||
|
||||
if (ZSTR_LEN(result) < 13) {
|
||||
zend_string_free(result);
|
||||
RETURN_FALSE;
|
||||
@ -533,51 +510,50 @@ PHP_FUNCTION(password_hash)
|
||||
#if HAVE_ARGON2LIB
|
||||
case PHP_PASSWORD_ARGON2I:
|
||||
{
|
||||
size_t out_len = 32;
|
||||
size_t encoded_len;
|
||||
int status = 0;
|
||||
char *out;
|
||||
zend_string *out = zend_string_alloc(32, 0);
|
||||
zend_string *encoded;
|
||||
|
||||
encoded_len = argon2_encodedlen(
|
||||
time_cost,
|
||||
memory_cost,
|
||||
threads,
|
||||
(uint32_t)salt_len,
|
||||
out_len
|
||||
(uint32_t)ZSTR_LEN(salt),
|
||||
ZSTR_LEN(out)
|
||||
#if HAVE_ARGON2ID
|
||||
, type
|
||||
#endif
|
||||
);
|
||||
|
||||
out = emalloc(out_len + 1);
|
||||
encoded = zend_string_alloc(encoded_len, 0);
|
||||
|
||||
status = argon2_hash(
|
||||
time_cost,
|
||||
memory_cost,
|
||||
threads,
|
||||
password,
|
||||
password_len,
|
||||
salt,
|
||||
salt_len,
|
||||
out,
|
||||
out_len,
|
||||
ZSTR_VAL(password),
|
||||
ZSTR_LEN(password),
|
||||
ZSTR_VAL(salt),
|
||||
ZSTR_LEN(salt),
|
||||
ZSTR_VAL(out),
|
||||
ZSTR_LEN(out),
|
||||
ZSTR_VAL(encoded),
|
||||
encoded_len,
|
||||
ZSTR_LEN(encoded),
|
||||
type,
|
||||
ARGON2_VERSION_NUMBER
|
||||
);
|
||||
|
||||
efree(out);
|
||||
efree(salt);
|
||||
zend_string_release(out);
|
||||
zend_string_release(salt);
|
||||
|
||||
if (status != ARGON2_OK) {
|
||||
zend_string_free(encoded);
|
||||
php_error_docref(NULL, E_WARNING, argon2_error_message(status));
|
||||
php_error(E_WARNING, "%s", argon2_error_message(status));
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
|
||||
ZSTR_VAL(encoded)[ZSTR_LEN(encoded)] = 0;
|
||||
RETURN_STR(encoded);
|
||||
}
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user