Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make mcrypt input handling stricter #610

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
182 changes: 115 additions & 67 deletions ext/mcrypt/mcrypt.c
Expand Up @@ -40,6 +40,7 @@
#include "php_globals.h" #include "php_globals.h"
#include "ext/standard/info.h" #include "ext/standard/info.h"
#include "ext/standard/php_rand.h" #include "ext/standard/php_rand.h"
#include "ext/standard/php_smart_str.h"
#include "php_mcrypt_filter.h" #include "php_mcrypt_filter.h"


static int le_mcrypt; static int le_mcrypt;
Expand Down Expand Up @@ -658,7 +659,7 @@ PHP_FUNCTION(mcrypt_generic)
char *data; char *data;
int data_len; int data_len;
php_mcrypt *pm; php_mcrypt *pm;
unsigned char* data_s; char* data_s;
int block_size, data_size; int block_size, data_size;


if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &mcryptind, &data, &data_len) == FAILURE) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &mcryptind, &data, &data_len) == FAILURE) {
Expand Down Expand Up @@ -1165,14 +1166,95 @@ PHP_FUNCTION(mcrypt_get_cipher_name)
} }
/* }}} */ /* }}} */


static void php_mcrypt_do_crypt(char* cipher, const char *key, int key_len, const char *data, int data_len, char *mode, const char *iv, int iv_len, int argc, int dencrypt, zval* return_value TSRMLS_DC) /* {{{ */ static char *php_mcrypt_get_key_size_str(
int max_key_size, const int *key_sizes, int key_size_count) /* {{{ */
{
if (key_size_count == 0) {
char *str;
spprintf(&str, 0, "Only keys of size 1 to %d supported", max_key_size);
return str;
} else if (key_size_count == 1) {
char *str;
spprintf(&str, 0, "Only keys of size %d supported", key_sizes[0]);
return str;
} else {
int i;
smart_str str = {0};
smart_str_appends(&str, "Only keys of sizes ");

for (i = 0; i < key_size_count; ++i) {
if (i == key_size_count - 1) {
smart_str_appends(&str, " or ");
} else if (i != 0) {
smart_str_appends(&str, ", ");
}

smart_str_append_long(&str, key_sizes[i]);
}

smart_str_appends(&str, " supported");
smart_str_0(&str);
return str.c;
}
}
/* }}} */

static zend_bool php_mcrypt_is_valid_key_size(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

determine (extra r)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's in a deleted piece of code ;)

int key_size, int max_key_size, int *key_sizes, int key_size_count) /* {{{ */
{
int i;

if (key_size <= 0 || key_size > max_key_size) {
return 0;
}

if (key_size_count == 0) {
/* All key sizes are valid */
return 1;
}

for (i = 0; i < key_size_count; i++) {
if (key_sizes[i] == key_size) {
return 1;
}
}

return 0;
}
/* }}} */

static int php_mcrypt_ensure_valid_key_size(MCRYPT td, int key_size TSRMLS_DC) /* {{{ */
{
int key_size_count;
int max_key_size = mcrypt_enc_get_key_size(td);
int *key_sizes = mcrypt_enc_get_supported_key_sizes(td, &key_size_count);

zend_bool is_valid_key_size = php_mcrypt_is_valid_key_size(
key_size, max_key_size, key_sizes, key_size_count
);
if (!is_valid_key_size) {
char *key_size_str = php_mcrypt_get_key_size_str(
max_key_size, key_sizes, key_size_count
);
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Key of size %d not supported by this algorithm. %s", key_size, key_size_str
);
efree(key_size_str);
}

if (key_sizes) {
mcrypt_free(key_sizes);
}

return is_valid_key_size ? SUCCESS : FAILURE;
}
/* }}} */

static void php_mcrypt_do_crypt(char* cipher, const char *key, int key_len, const char *data, int data_len, char *mode, const char *iv, int iv_len, int dencrypt, zval* return_value TSRMLS_DC) /* {{{ */
{ {
char *cipher_dir_string; char *cipher_dir_string;
char *module_dir_string; char *module_dir_string;
int block_size, max_key_length, use_key_length, i, count, iv_size;
unsigned long int data_size; unsigned long int data_size;
int *key_length_sizes;
char *key_s = NULL, *iv_s;
char *data_s; char *data_s;
MCRYPT td; MCRYPT td;


Expand All @@ -1183,92 +1265,58 @@ static void php_mcrypt_do_crypt(char* cipher, const char *key, int key_len, cons
php_error_docref(NULL TSRMLS_CC, E_WARNING, MCRYPT_OPEN_MODULE_FAILED); php_error_docref(NULL TSRMLS_CC, E_WARNING, MCRYPT_OPEN_MODULE_FAILED);
RETURN_FALSE; RETURN_FALSE;
} }

/* Checking for key-length */ /* Checking for key-length */
max_key_length = mcrypt_enc_get_key_size(td); if (php_mcrypt_ensure_valid_key_size(td, key_len TSRMLS_CC) == FAILURE) {
if (key_len > max_key_length) { mcrypt_module_close(td);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size of key is too large for this algorithm"); RETURN_FALSE;
}
key_length_sizes = mcrypt_enc_get_supported_key_sizes(td, &count);
if (count == 0 && key_length_sizes == NULL) { /* all lengths 1 - k_l_s = OK */
use_key_length = key_len;
key_s = emalloc(use_key_length);
memset(key_s, 0, use_key_length);
memcpy(key_s, key, use_key_length);
} else if (count == 1) { /* only m_k_l = OK */
key_s = emalloc(key_length_sizes[0]);
memset(key_s, 0, key_length_sizes[0]);
memcpy(key_s, key, MIN(key_len, key_length_sizes[0]));
use_key_length = key_length_sizes[0];
} else { /* dertermine smallest supported key > length of requested key */
use_key_length = max_key_length; /* start with max key length */
for (i = 0; i < count; i++) {
if (key_length_sizes[i] >= key_len &&
key_length_sizes[i] < use_key_length)
{
use_key_length = key_length_sizes[i];
}
}
key_s = emalloc(use_key_length);
memset(key_s, 0, use_key_length);
memcpy(key_s, key, MIN(key_len, use_key_length));
} }
mcrypt_free (key_length_sizes);

/* Check IV */
iv_s = NULL;
iv_size = mcrypt_enc_get_iv_size (td);


/* IV is required */ /* IV is required */
if (mcrypt_enc_mode_has_iv(td) == 1) { if (mcrypt_enc_mode_has_iv(td) == 1) {
if (argc == 5) { if (!iv) {
if (iv_size != iv_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Encryption mode requires an initialization vector");
php_error_docref(NULL TSRMLS_CC, E_WARNING, MCRYPT_IV_WRONG_SIZE); mcrypt_module_close(td);
} else { RETURN_FALSE;
iv_s = emalloc(iv_size + 1); }
memcpy(iv_s, iv, iv_size);
} if (iv_len != mcrypt_enc_get_iv_size(td)) {
} else if (argc == 4) { php_error_docref(NULL TSRMLS_CC, E_WARNING, MCRYPT_IV_WRONG_SIZE);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Attempt to use an empty IV, which is NOT recommend"); mcrypt_module_close(td);
iv_s = emalloc(iv_size + 1); RETURN_FALSE;
memset(iv_s, 0, iv_size + 1);
} }
} }


/* Check blocksize */ /* Check blocksize */
if (mcrypt_enc_is_block_mode(td) == 1) { /* It's a block algorithm */ if (mcrypt_enc_is_block_mode(td) == 1) { /* It's a block algorithm */
block_size = mcrypt_enc_get_block_size(td); int block_size = mcrypt_enc_get_block_size(td);
data_size = (((data_len - 1) / block_size) + 1) * block_size; data_size = (((data_len - 1) / block_size) + 1) * block_size;
data_s = emalloc(data_size); data_s = emalloc(data_size + 1);
memset(data_s, 0, data_size); memset(data_s, 0, data_size);
memcpy(data_s, data, data_len); memcpy(data_s, data, data_len);
} else { /* It's not a block algorithm */ } else { /* It's not a block algorithm */
data_size = data_len; data_size = data_len;
data_s = emalloc(data_size); data_s = emalloc(data_size + 1);
memset(data_s, 0, data_size);
memcpy(data_s, data, data_len); memcpy(data_s, data, data_len);
} }


if (mcrypt_generic_init(td, key_s, use_key_length, iv_s) < 0) { if (mcrypt_generic_init(td, (void *) key, key_len, (void *) iv) < 0) {
php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Mcrypt initialisation failed"); php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Mcrypt initialisation failed");
mcrypt_module_close(td);
RETURN_FALSE; RETURN_FALSE;
} }

if (dencrypt == MCRYPT_ENCRYPT) { if (dencrypt == MCRYPT_ENCRYPT) {
mcrypt_generic(td, data_s, data_size); mcrypt_generic(td, data_s, data_size);
} else { } else {
mdecrypt_generic(td, data_s, data_size); mdecrypt_generic(td, data_s, data_size);
} }


RETVAL_STRINGL(data_s, data_size, 1); data_s[data_size] = 0;
RETVAL_STRINGL(data_s, data_size, 0);


/* freeing vars */ /* freeing vars */
mcrypt_generic_end(td); mcrypt_generic_end(td);
if (key_s != NULL) {
efree (key_s);
}
if (iv_s != NULL) {
efree (iv_s);
}
efree (data_s);
} }
/* }}} */ /* }}} */


Expand All @@ -1284,7 +1332,7 @@ PHP_FUNCTION(mcrypt_encrypt)


convert_to_string_ex(mode); convert_to_string_ex(mode);


php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, Z_STRVAL_PP(mode), iv, iv_len, ZEND_NUM_ARGS(), MCRYPT_ENCRYPT, return_value TSRMLS_CC); php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, Z_STRVAL_PP(mode), iv, iv_len, MCRYPT_ENCRYPT, return_value TSRMLS_CC);
} }
/* }}} */ /* }}} */


Expand All @@ -1300,7 +1348,7 @@ PHP_FUNCTION(mcrypt_decrypt)


convert_to_string_ex(mode); convert_to_string_ex(mode);


php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, Z_STRVAL_PP(mode), iv, iv_len, ZEND_NUM_ARGS(), MCRYPT_DECRYPT, return_value TSRMLS_CC); php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, Z_STRVAL_PP(mode), iv, iv_len, MCRYPT_DECRYPT, return_value TSRMLS_CC);
} }
/* }}} */ /* }}} */


Expand All @@ -1316,7 +1364,7 @@ PHP_FUNCTION(mcrypt_ecb)


convert_to_long_ex(mode); convert_to_long_ex(mode);


php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "ecb", iv, iv_len, ZEND_NUM_ARGS(), Z_LVAL_PP(mode), return_value TSRMLS_CC); php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "ecb", iv, iv_len, Z_LVAL_PP(mode), return_value TSRMLS_CC);
} }
/* }}} */ /* }}} */


Expand All @@ -1332,7 +1380,7 @@ PHP_FUNCTION(mcrypt_cbc)


convert_to_long_ex(mode); convert_to_long_ex(mode);


php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "cbc", iv, iv_len, ZEND_NUM_ARGS(), Z_LVAL_PP(mode), return_value TSRMLS_CC); php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "cbc", iv, iv_len, Z_LVAL_PP(mode), return_value TSRMLS_CC);
} }
/* }}} */ /* }}} */


Expand All @@ -1348,7 +1396,7 @@ PHP_FUNCTION(mcrypt_cfb)


convert_to_long_ex(mode); convert_to_long_ex(mode);


php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "cfb", iv, iv_len, ZEND_NUM_ARGS(), Z_LVAL_PP(mode), return_value TSRMLS_CC); php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "cfb", iv, iv_len, Z_LVAL_PP(mode), return_value TSRMLS_CC);
} }
/* }}} */ /* }}} */


Expand All @@ -1364,7 +1412,7 @@ PHP_FUNCTION(mcrypt_ofb)


convert_to_long_ex(mode); convert_to_long_ex(mode);


php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "ofb", iv, iv_len, ZEND_NUM_ARGS(), Z_LVAL_PP(mode), return_value TSRMLS_CC); php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "ofb", iv, iv_len, Z_LVAL_PP(mode), return_value TSRMLS_CC);
} }
/* }}} */ /* }}} */


Expand Down
13 changes: 7 additions & 6 deletions ext/mcrypt/tests/bug46010.phpt
Expand Up @@ -5,12 +5,13 @@ Bug #46010 (warnings incorrectly generated for iv in ecb mode)
--FILE-- --FILE--
<?php <?php


var_dump(bin2hex(mcrypt_encrypt(MCRYPT_TRIPLEDES, "key", "data", MCRYPT_MODE_ECB))); $key = "012345678901234567890123";
var_dump(bin2hex(mcrypt_encrypt(MCRYPT_TRIPLEDES, "key", "data", MCRYPT_MODE_ECB, "a"))); var_dump(bin2hex(mcrypt_encrypt(MCRYPT_TRIPLEDES, $key, "data", MCRYPT_MODE_ECB)));
var_dump(bin2hex(mcrypt_encrypt(MCRYPT_TRIPLEDES, "key", "data", MCRYPT_MODE_ECB, "12345678"))); var_dump(bin2hex(mcrypt_encrypt(MCRYPT_TRIPLEDES, $key, "data", MCRYPT_MODE_ECB, "a")));
var_dump(bin2hex(mcrypt_encrypt(MCRYPT_TRIPLEDES, $key, "data", MCRYPT_MODE_ECB, "12345678")));


?> ?>
--EXPECTF-- --EXPECTF--
string(16) "372eeb4a524b8d31" string(16) "f7a2ce11d4002294"
string(16) "372eeb4a524b8d31" string(16) "f7a2ce11d4002294"
string(16) "372eeb4a524b8d31" string(16) "f7a2ce11d4002294"
8 changes: 5 additions & 3 deletions ext/mcrypt/tests/mcrypt_cbc.phpt
Expand Up @@ -4,7 +4,7 @@ mcrypt_cbc
<?php if (!extension_loaded("mcrypt")) print "skip"; ?> <?php if (!extension_loaded("mcrypt")) print "skip"; ?>
--FILE-- --FILE--
<?php <?php
$key = "FooBar"; $key = "0123456789012345";
$secret = "PHP Testfest 2008"; $secret = "PHP Testfest 2008";
$cipher = MCRYPT_RIJNDAEL_128; $cipher = MCRYPT_RIJNDAEL_128;


Expand All @@ -15,8 +15,9 @@ $enc_data = mcrypt_cbc($cipher, $key, $secret, MCRYPT_ENCRYPT, $iv);
echo trim(mcrypt_cbc($cipher, $key, $enc_data, MCRYPT_DECRYPT, $iv)) . "\n"; echo trim(mcrypt_cbc($cipher, $key, $enc_data, MCRYPT_DECRYPT, $iv)) . "\n";


// a warning must be issued if we don't use a IV on a AES cipher, that usually requires an IV // a warning must be issued if we don't use a IV on a AES cipher, that usually requires an IV
mcrypt_cbc($cipher, $key, $enc_data, MCRYPT_DECRYPT); var_dump(mcrypt_cbc($cipher, $key, $enc_data, MCRYPT_DECRYPT));


?>
--EXPECTF-- --EXPECTF--


Deprecated: Function mcrypt_cbc() is deprecated in %s on line %d Deprecated: Function mcrypt_cbc() is deprecated in %s on line %d
Expand All @@ -26,4 +27,5 @@ PHP Testfest 2008


Deprecated: Function mcrypt_cbc() is deprecated in %s on line %d Deprecated: Function mcrypt_cbc() is deprecated in %s on line %d


Warning: mcrypt_cbc(): Attempt to use an empty IV, which is NOT recommend in %s on line %d Warning: mcrypt_cbc(): Encryption mode requires an initialization vector in %s on line %d
bool(false)