diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 9e35a5e020b..bf6f9b0b583 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1872,6 +1872,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_password_hash, 0, 0, 1) ZEND_ARG_INFO(0, algo) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_password_needs_rehash, 0, 0, 1) + ZEND_ARG_INFO(0, hash) + ZEND_ARG_INFO(0, algo) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_password_verify, 0, 0, 2) ZEND_ARG_INFO(0, password) ZEND_ARG_INFO(0, hash) @@ -2896,6 +2901,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(base64_encode, arginfo_base64_encode) PHP_FE(password_hash, arginfo_password_hash) + PHP_FE(password_needs_rehash, arginfo_password_needs_rehash) PHP_FE(password_verify, arginfo_password_verify) PHP_FE(password_make_salt, arginfo_password_make_salt) diff --git a/ext/standard/password.c b/ext/standard/password.c index eb4abd2722f..9bfb0235843 100644 --- a/ext/standard/password.c +++ b/ext/standard/password.c @@ -43,6 +43,18 @@ PHP_MINIT_FUNCTION(password) /* {{{ */ } /* }}} */ +static long php_password_determine_algo(const char *hash, const int len) +{ + if (len < 3) { + return 0; + } + if (hash[0] == '$' && hash[1] == '2' && hash[2] == 'y' && len == 60) { + return PHP_PASSWORD_BCRYPT; + } + + return 0; +} + static int php_password_salt_is_alphabet(const char *str, const int len) /* {{{ */ { int i = 0; @@ -149,6 +161,44 @@ static int php_password_make_salt(long length, int raw, char *ret TSRMLS_DC) /* } /* }}} */ +PHP_FUNCTION(password_needs_rehash) +{ + long new_algo = 0, algo = 0; + int hash_len; + char *hash; + HashTable *options = 0; + zval **option_buffer; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|H", &hash, &hash_len, &new_algo, &options) == FAILURE) { + RETURN_NULL(); + } + algo = php_password_determine_algo(hash, hash_len); + + if (algo != new_algo) { + RETURN_TRUE; + } + + switch (algo) { + case PHP_PASSWORD_BCRYPT: + { + int newCost = PHP_PASSWORD_BCRYPT_COST, cost = 0; + + if (options && zend_symtable_find(options, "cost", 5, (void **) &option_buffer) == SUCCESS) { + convert_to_long_ex(option_buffer); + newCost = Z_LVAL_PP(option_buffer); + zval_ptr_dtor(option_buffer); + } + + sscanf(hash, "$2y$%d$", &cost); + if (cost != newCost) { + RETURN_TRUE; + } + } + break; + } + RETURN_FALSE; +} + /* {{{ proto boolean password_make_salt(string password, string hash) Verify a hash created using crypt() or password_hash() */ PHP_FUNCTION(password_verify) diff --git a/ext/standard/php_password.h b/ext/standard/php_password.h index 57c6b887858..45e68499368 100644 --- a/ext/standard/php_password.h +++ b/ext/standard/php_password.h @@ -24,6 +24,7 @@ PHP_FUNCTION(password_hash); PHP_FUNCTION(password_verify); PHP_FUNCTION(password_make_salt); +PHP_FUNCTION(password_needs_rehash); PHP_MINIT_FUNCTION(password);