]> granicus.if.org Git - php/commitdiff
Base structure for passsword_create and password_make_salt
authorAnthony Ferrara <ircmaxell@ircmaxell.com>
Mon, 25 Jun 2012 02:44:43 +0000 (22:44 -0400)
committerAnthony Ferrara <ircmaxell@ircmaxell.com>
Mon, 25 Jun 2012 02:44:43 +0000 (22:44 -0400)
ext/standard/basic_functions.c
ext/standard/config.m4
ext/standard/config.w32
ext/standard/password.c [new file with mode: 0644]
ext/standard/php_password.h [new file with mode: 0644]
ext/standard/php_standard.h

index 63d40efde440005fefc4b9018025a0ff383a02e9..64025db6257291603f7b1031cad6878e52229a33 100644 (file)
@@ -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)
index c33ae1e05c897262f57500c1293ba1a7e92bc896..fba423b19195ae8716fe16b7b840b06a4e013b4f 100644 (file)
@@ -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/])
index d14b859e9db997ac618d63f6da5e9d4b00ffb7aa..5f24641b4d33a742986fd06214194dadd0fb9f66 100644 (file)
@@ -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 (file)
index 0000000..677f132
--- /dev/null
@@ -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 (file)
index 0000000..f813189
--- /dev/null
@@ -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:
+ */
index 483dbc33bc80a2fdbd96a549b430d2a06c414e44..bccfebe54331f98cb71f060c6ea697448b51238c 100644 (file)
@@ -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);