diff options
author | Anthony Ferrara <ircmaxell@ircmaxell.com> | 2012-06-24 22:44:43 -0400 |
---|---|---|
committer | Anthony Ferrara <ircmaxell@ircmaxell.com> | 2012-06-24 22:44:43 -0400 |
commit | c77f2c29585f97bd9dad533b9d2bc8334de34f1b (patch) | |
tree | ec9ed12ba5534fbe78219daae054e9e8b2913804 | |
parent | d68b614b09b984e915db50b72430db4e4731480c (diff) | |
download | php-git-c77f2c29585f97bd9dad533b9d2bc8334de34f1b.tar.gz |
Base structure for passsword_create and password_make_salt
-rw-r--r-- | ext/standard/basic_functions.c | 20 | ||||
-rw-r--r-- | ext/standard/config.m4 | 2 | ||||
-rw-r--r-- | ext/standard/config.w32 | 2 | ||||
-rw-r--r-- | ext/standard/password.c | 257 | ||||
-rw-r--r-- | ext/standard/php_password.h | 48 | ||||
-rw-r--r-- | ext/standard/php_standard.h | 1 |
6 files changed, 328 insertions, 2 deletions
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 63d40efde4..64025db625 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1866,6 +1866,21 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getlastmod, 0) ZEND_END_ARG_INFO() /* }}} */ +/* {{{ password.c */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_password_create, 0, 0, 1) + ZEND_ARG_INFO(0, password) + 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) +ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_password_make_salt, 0, 0, 1) + ZEND_ARG_INFO(0, length) + ZEND_ARG_INFO(0, raw_output) +ZEND_END_ARG_INFO() +/* }}} */ /* {{{ proc_open.c */ #ifdef PHP_CAN_SUPPORT_PROC_OPEN ZEND_BEGIN_ARG_INFO_EX(arginfo_proc_terminate, 0, 0, 1) @@ -2880,6 +2895,10 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(base64_decode, arginfo_base64_decode) PHP_FE(base64_encode, arginfo_base64_encode) + PHP_FE(password_create, arginfo_password_create) + PHP_FE(password_verify, arginfo_password_verify) + PHP_FE(password_make_salt, arginfo_password_make_salt) + PHP_FE(convert_uuencode, arginfo_convert_uuencode) PHP_FE(convert_uudecode, arginfo_convert_uudecode) @@ -3630,6 +3649,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ BASIC_MINIT_SUBMODULE(browscap) BASIC_MINIT_SUBMODULE(standard_filters) BASIC_MINIT_SUBMODULE(user_filters) + BASIC_MINIT_SUBMODULE(password) #if defined(HAVE_LOCALECONV) && defined(ZTS) BASIC_MINIT_SUBMODULE(localeconv) diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index c33ae1e05c..fba423b191 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -580,7 +580,7 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32. incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \ http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \ var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \ - filters.c proc_open.c streamsfuncs.c http.c) + filters.c proc_open.c streamsfuncs.c http.c password.c) PHP_ADD_MAKEFILE_FRAGMENT PHP_INSTALL_HEADERS([ext/standard/]) diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index d14b859e9d..5f24641b4d 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -19,7 +19,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \ versioning.c assert.c strnatcmp.c levenshtein.c incomplete_class.c \ url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \ php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \ - user_filters.c uuencode.c filters.c proc_open.c \ + user_filters.c uuencode.c filters.c proc_open.c password.c \ streamsfuncs.c http.c flock_compat.c", false /* never shared */); PHP_INSTALL_HEADERS("", "ext/standard"); if (PHP_MBREGEX != "no") { diff --git a/ext/standard/password.c b/ext/standard/password.c new file mode 100644 index 0000000000..677f1325ca --- /dev/null +++ b/ext/standard/password.c @@ -0,0 +1,257 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2012 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Anthony Ferrara <ircmaxell@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include <stdlib.h> + +#include "php.h" +#if HAVE_CRYPT +#include "php_crypt.h" +#endif + +#include "php_password.h" +#include "php_rand.h" +#include "base64.h" + + +PHP_MINIT_FUNCTION(password) /* {{{ */ +{ + REGISTER_STRING_CONSTANT("PASSWORD_DEFAULT", PHP_PASSWORD_DEFAULT, CONST_CS | CONST_PERSISTENT); + REGISTER_STRING_CONSTANT("PASSWORD_BCRYPT", PHP_PASSWORD_BCRYPT, CONST_CS | CONST_PERSISTENT); + REGISTER_STRING_CONSTANT("PASSWORD_MD5", PHP_PASSWORD_MD5, CONST_CS | CONST_PERSISTENT); + REGISTER_STRING_CONSTANT("PASSWORD_SHA256", PHP_PASSWORD_SHA256, CONST_CS | CONST_PERSISTENT); + REGISTER_STRING_CONSTANT("PASSWORD_SHA512", PHP_PASSWORD_SHA512, CONST_CS | CONST_PERSISTENT); + return SUCCESS; +} +/* }}} */ + + +static int php_password_salt_is_alphabet(const char *str, const int len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (!((str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= '0' && str[i] <= '9') || str[i] == '.' || str[i] == '/')) { + return 0; + } + } + return 1; +} + +static int php_password_salt_to64(const char *str, const int str_len, const int out_len, char *ret) +{ + int pos = 0; + unsigned char *buffer; + buffer = php_base64_encode((unsigned char*) str, str_len, NULL); + for (pos = 0; pos < out_len; pos++) { + if (buffer[pos] == '+') { + ret[pos] = '.'; + } else if (buffer[pos] == '=') { + efree(buffer); + return FAILURE; + } else { + ret[pos] = buffer[pos]; + } + } + efree(buffer); + return SUCCESS; +} + +static int php_password_make_salt(int length, int raw, char *ret) +{ + int i, raw_length; + char *buffer; + if (raw) { + raw_length = length; + } else { + raw_length = length * 3 / 4 + 1; + } + buffer = (char *) emalloc(raw_length + 1); + + /* Temp Placeholder */ + for (i = 0; i < raw_length; i++) { + buffer[i] = i; + } + /* /Temp Placeholder */ + + if (raw) { + memcpy(ret, buffer, length); + } else { + char *result; + result = emalloc(length + 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; + } else { + memcpy(ret, result, length); + efree(result); + } + } + efree(buffer); + ret[length] = 0; + return SUCCESS; +} + +PHP_FUNCTION(password_verify) +{ +} + +PHP_FUNCTION(password_make_salt) +{ + char *salt; + int length = 0; + zend_bool raw_output = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|b", &length, &raw_output) == FAILURE) { + RETURN_FALSE; + } + if (length <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length cannot be less than or equal zero: %d", length); + RETURN_FALSE; + } + salt = emalloc(length + 1); + if (php_password_make_salt(length, (int) raw_output, salt) == FAILURE) { + efree(salt); + RETURN_FALSE; + } + RETURN_STRINGL(salt, length, 0); +} + + +/* {{{ proto string password(string password, string algo = PASSWORD_DEFAULT, array options = array()) +Hash a password */ +PHP_FUNCTION(password_create) +{ + char *password, *algo = 0, *hash_format, *hash, *salt; + int password_len, algo_len = 0, salt_len = 0, required_salt_len = 0, hash_format_len; + HashTable *options = 0; + zval **option_buffer; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sH", &password, &password_len, &algo, &algo_len, &options) == FAILURE) { + RETURN_FALSE; + } + + if (algo_len == 0) { + algo = PHP_PASSWORD_DEFAULT; + algo_len = strlen(PHP_PASSWORD_DEFAULT); + } + + if (strcmp(algo, PHP_PASSWORD_BCRYPT) == 0) { + int cost = PHP_PASSWORD_BCRYPT_DEFAULT_COST; + if (options && zend_symtable_find(options, "cost", 5, (void **) &option_buffer) == SUCCESS) { + convert_to_long_ex(option_buffer); + cost = Z_LVAL_PP(option_buffer); + zval_ptr_dtor(option_buffer); + if (cost < 4 || cost > 31) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid bcrypt cost parameter specified: %d", cost); + RETURN_FALSE; + } + } + required_salt_len = 22; + hash_format = emalloc(8); + sprintf(hash_format, "$2y$%02d$", cost); + hash_format_len = 7; + } else if (strcmp(algo, PHP_PASSWORD_MD5) == 0) { + required_salt_len = 12; + hash_format = emalloc(4); + memcpy(hash_format, "$1$", 3); + hash_format_len = 3; + } else if (strcmp(algo, PHP_PASSWORD_SHA256) == 0 || strcmp(algo, PHP_PASSWORD_SHA512) == 0) { + int rounds = PHP_PASSWORD_SHA_DEFAULT_ROUNDS; + if (options && zend_symtable_find(options, "rounds", 7, (void **) &option_buffer) == SUCCESS) { + convert_to_long_ex(option_buffer); + rounds = Z_LVAL_PP(option_buffer); + zval_ptr_dtor(option_buffer); + if (rounds < 1000 || rounds > 999999999) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid SHA rounds parameter specified: %d", rounds); + RETURN_FALSE; + } + } + required_salt_len = 16; + hash_format = emalloc(21); + sprintf(hash_format, "$%s$rounds=%d$", algo, rounds); + hash_format_len = strlen(hash_format); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown password hashing algorithm: %s", algo); + RETURN_FALSE; + } + + if (options && zend_symtable_find(options, "salt", 5, (void**) &option_buffer) == SUCCESS) { + char *buffer; + int buffer_len; + if (Z_TYPE_PP(option_buffer) == IS_STRING) { + buffer = Z_STRVAL_PP(option_buffer); + buffer_len = Z_STRLEN_PP(option_buffer); + } else { + zval_ptr_dtor(option_buffer); + efree(hash_format); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Non-string salt parameter supplied"); + RETURN_FALSE; + } + if (buffer_len < required_salt_len) { + efree(hash_format); + zval_ptr_dtor(option_buffer); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Provided salt is too short: %d expecting %d", buffer_len, required_salt_len); + RETURN_FALSE; + } else if (0 == php_password_salt_is_alphabet(buffer, buffer_len)) { + salt = emalloc(required_salt_len + 1); + if (php_password_salt_to64(buffer, buffer_len, required_salt_len, salt) == FAILURE) { + efree(hash_format); + zval_ptr_dtor(option_buffer); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Provided salt is too short: %d", salt_len); + RETURN_FALSE; + } + salt_len = required_salt_len; + } else { + salt = emalloc(required_salt_len + 1); + memcpy(salt, buffer, required_salt_len); + salt_len = required_salt_len; + } + zval_ptr_dtor(option_buffer); + } else { + salt = emalloc(required_salt_len + 1); + if (php_password_make_salt(required_salt_len, 0, salt) == FAILURE) { + efree(hash_format); + efree(salt); + RETURN_FALSE; + } + salt_len = required_salt_len; + } + + salt[salt_len] = 0; + + hash = emalloc(salt_len + hash_format_len + 1); + sprintf(hash, "%s%s", hash_format, salt); + hash[hash_format_len + salt_len] = 0; + efree(hash_format); + efree(salt); + + RETURN_STRINGL(hash, hash_format_len + salt_len, 0); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/standard/php_password.h b/ext/standard/php_password.h new file mode 100644 index 0000000000..f813189a46 --- /dev/null +++ b/ext/standard/php_password.h @@ -0,0 +1,48 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2012 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Anthony Ferrara <ircmaxell@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_PASSWORD_H +#define PHP_PASSWORD_H + +PHP_FUNCTION(password_create); +PHP_FUNCTION(password_verify); +PHP_FUNCTION(password_make_salt); + +PHP_MINIT_FUNCTION(password); + +#define PHP_PASSWORD_DEFAULT "2y" +#define PHP_PASSWORD_BCRYPT "2y" +#define PHP_PASSWORD_MD5 "1" +#define PHP_PASSWORD_SHA256 "5" +#define PHP_PASSWORD_SHA512 "6" + +#define PHP_PASSWORD_BCRYPT_DEFAULT_COST 14; +#define PHP_PASSWORD_SHA_DEFAULT_ROUNDS 5000; + + +#endif + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/standard/php_standard.h b/ext/standard/php_standard.h index 483dbc33bc..bccfebe543 100644 --- a/ext/standard/php_standard.h +++ b/ext/standard/php_standard.h @@ -58,6 +58,7 @@ #include "php_versioning.h" #include "php_ftok.h" #include "php_type.h" +#include "php_password.h" #define phpext_standard_ptr basic_functions_module_ptr PHP_MINIT_FUNCTION(standard_filters); |