Skip to content

Commit 9e63761

Browse files
committed
revert to initial fix
1 parent c5e9672 commit 9e63761

File tree

10 files changed

+59
-93
lines changed

10 files changed

+59
-93
lines changed

NEWS

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,8 @@ PHP NEWS
138138
while COW violation flag is still set). (alexandre-daubois)
139139
. Added form feed (\f) in the default trimmed characters of trim(), rtrim()
140140
and ltrim(). (Weilin Du)
141-
. Fixed bug GH-21673 (the bundled bcrypt implementation no longer truncates
142-
passwords at NUL bytes, and password_hash()/password_verify() no longer
143-
reject them). (Weilin Du)
141+
. Fixed bug GH-21673 Reject NUL bytes in bcrypt passwords passed to
142+
password_verify(). (Weilin Du)
144143
. Invalid mode values now throw in array_filter() instead of being silently
145144
defaulted to 0. (Jorg Sowa)
146145
. Fixed bug GH-21058 (error_log() crashes with message_type 3 and

ext/standard/crypt.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */
6767
}
6868
/* }}} */
6969

70-
PHPAPI zend_string *php_crypt(const char *password, size_t pass_len, const char *salt, int salt_len, bool quiet)
70+
PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, bool quiet)
7171
{
7272
char *crypt_res;
7373
zend_string *result;
@@ -129,7 +129,7 @@ PHPAPI zend_string *php_crypt(const char *password, size_t pass_len, const char
129129

130130
memset(output, 0, PHP_MAX_SALT_LEN + 1);
131131

132-
crypt_res = php_crypt_blowfish_rn(password, pass_len, salt, output, sizeof(output));
132+
crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output));
133133
if (!crypt_res) {
134134
ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
135135
return NULL;
@@ -206,8 +206,8 @@ PHP_FUNCTION(crypt)
206206
zend_string *result;
207207

208208
ZEND_PARSE_PARAMETERS_START(2, 2)
209-
Z_PARAM_PATH(str, str_len)
210-
Z_PARAM_PATH(salt_in, salt_in_len)
209+
Z_PARAM_STRING(str, str_len)
210+
Z_PARAM_STRING(salt_in, salt_in_len)
211211
ZEND_PARSE_PARAMETERS_END();
212212

213213
salt[0] = salt[PHP_MAX_SALT_LEN] = '\0';
@@ -220,7 +220,7 @@ PHP_FUNCTION(crypt)
220220
salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len);
221221
salt[salt_in_len] = '\0';
222222

223-
if ((result = php_crypt(str, str_len, salt, (int)salt_in_len, 0)) == NULL) {
223+
if ((result = php_crypt(str, (int)str_len, salt, (int)salt_in_len, 0)) == NULL) {
224224
if (salt[0] == '*' && salt[1] == '0') {
225225
RETURN_STRING("*1");
226226
} else {

ext/standard/crypt_blowfish.c

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -530,10 +530,10 @@ static void BF_swap(BF_word *x, int count)
530530
*(ptr - 1) = R; \
531531
} while (ptr < &data.ctx.S[3][0xFF]);
532532

533-
static void BF_set_key(const char *key, size_t key_len, BF_key expanded,
534-
BF_key initial, unsigned char flags)
533+
static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
534+
unsigned char flags)
535535
{
536-
size_t key_pos = 0;
536+
const char *ptr = key;
537537
unsigned int bug, i, j;
538538
BF_word safety, sign, diff, tmp[2];
539539

@@ -559,7 +559,8 @@ static void BF_set_key(const char *key, size_t key_len, BF_key expanded,
559559
* information - that is, we mostly use fixed-cost bitwise operations instead
560560
* of branches or table lookups. (One conditional branch based on password
561561
* length remains. It is not part of the bug aftermath, though, and is
562-
* difficult and possibly unreasonable to avoid here.)
562+
* difficult and possibly unreasonable to avoid given the use of C strings by
563+
* the caller, which results in similar timing leaks anyway.)
563564
*
564565
* For actual implementation, we set an array index in the variable "bug"
565566
* (0 means no bug, 1 means sign extension bug emulation) and a flag in the
@@ -576,33 +577,25 @@ static void BF_set_key(const char *key, size_t key_len, BF_key expanded,
576577

577578
sign = diff = 0;
578579

579-
/*
580-
* bcrypt cycles over the password bytes plus a trailing NUL terminator.
581-
* The explicit length keeps embedded NUL bytes significant while
582-
* preserving the historical behavior for ordinary C strings.
583-
*/
584580
for (i = 0; i < BF_N + 2; i++) {
585581
tmp[0] = tmp[1] = 0;
586582
for (j = 0; j < 4; j++) {
587-
unsigned char c = key_pos < key_len ? (unsigned char) key[key_pos] : 0;
588-
589583
tmp[0] <<= 8;
590-
tmp[0] |= c; /* correct */
584+
tmp[0] |= (unsigned char)*ptr; /* correct */
591585
tmp[1] <<= 8;
592-
tmp[1] |= (BF_word_signed)(signed char)c; /* bug */
586+
tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
593587
/*
594588
* Sign extension in the first char has no effect - nothing to overwrite yet,
595589
* and those extra 24 bits will be fully shifted out of the 32-bit word. For
596590
* chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
597591
* extension in tmp[1] occurs. Once this flag is set, it remains set.
598592
*/
599-
if (j) {
593+
if (j)
600594
sign |= tmp[1] & 0x80;
601-
}
602-
key_pos++;
603-
if (key_pos > key_len) {
604-
key_pos = 0;
605-
}
595+
if (!*ptr)
596+
ptr = key;
597+
else
598+
ptr++;
606599
}
607600
diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
608601

@@ -643,7 +636,7 @@ static const unsigned char flags_by_subtype[26] =
643636
{2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
644637
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
645638

646-
static char *BF_crypt(const char *key, size_t key_len, const char *setting,
639+
static char *BF_crypt(const char *key, const char *setting,
647640
char *output, int size,
648641
BF_word min)
649642
{
@@ -686,7 +679,7 @@ static char *BF_crypt(const char *key, size_t key_len, const char *setting,
686679
}
687680
BF_swap(data.binary.salt, 4);
688681

689-
BF_set_key(key, key_len, data.expanded_key, data.ctx.P,
682+
BF_set_key(key, data.expanded_key, data.ctx.P,
690683
flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
691684

692685
memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
@@ -807,10 +800,10 @@ static int _crypt_output_magic(const char *setting, char *output, int size)
807800
* The performance cost of this quick self-test is around 0.6% at the "$2a$08"
808801
* setting.
809802
*/
810-
char *php_crypt_blowfish_rn(const char *key, size_t key_len,
811-
const char *setting, char *output, int size)
803+
char *php_crypt_blowfish_rn(const char *key, const char *setting,
804+
char *output, int size)
812805
{
813-
static const char test_key[] = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
806+
const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
814807
const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
815808
static const char * const test_hashes[2] =
816809
{"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */
@@ -826,7 +819,7 @@ char *php_crypt_blowfish_rn(const char *key, size_t key_len,
826819

827820
/* Hash the supplied password */
828821
_crypt_output_magic(setting, output, size);
829-
retval = BF_crypt(key, key_len, setting, output, size, 16);
822+
retval = BF_crypt(key, setting, output, size, 16);
830823
save_errno = errno;
831824

832825
/*
@@ -845,17 +838,17 @@ char *php_crypt_blowfish_rn(const char *key, size_t key_len,
845838
}
846839
memset(buf.o, 0x55, sizeof(buf.o));
847840
buf.o[sizeof(buf.o) - 1] = 0;
848-
p = BF_crypt(test_key, sizeof(test_key) - 1, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
841+
p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
849842

850843
ok = (p == buf.o &&
851844
!memcmp(p, buf.s, 7 + 22) &&
852845
!memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
853846

854847
{
855-
static const char k[] = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
848+
const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
856849
BF_key ae, ai, ye, yi;
857-
BF_set_key(k, sizeof(k) - 1, ae, ai, 2); /* $2a$ */
858-
BF_set_key(k, sizeof(k) - 1, ye, yi, 4); /* $2y$ */
850+
BF_set_key(k, ae, ai, 2); /* $2a$ */
851+
BF_set_key(k, ye, yi, 4); /* $2y$ */
859852
ai[0] ^= 0x10000; /* undo the safety (for comparison) */
860853
ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
861854
!memcmp(ae, ye, sizeof(ae)) &&

ext/standard/crypt_blowfish.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
#ifndef _CRYPT_BLOWFISH_H
1818
#define _CRYPT_BLOWFISH_H
1919

20-
#include <stddef.h>
21-
22-
extern char *php_crypt_blowfish_rn(const char *key, size_t key_len,
23-
const char *setting, char *output, int size);
20+
extern char *php_crypt_blowfish_rn(const char *key, const char *setting,
21+
char *output, int size);
2422

2523
#endif

ext/standard/password.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,12 @@ static bool php_password_bcrypt_needs_rehash(const zend_string *hash, zend_array
153153

154154
static bool php_password_bcrypt_verify(const zend_string *password, const zend_string *hash) {
155155
int status = 0;
156-
zend_string *ret = php_crypt(ZSTR_VAL(password), ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
156+
157+
if (zend_str_has_nul_byte(password)) {
158+
return false;
159+
}
160+
161+
zend_string *ret = php_crypt(ZSTR_VAL(password), (int)ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
157162

158163
if (!ret) {
159164
return false;
@@ -181,6 +186,11 @@ static zend_string* php_password_bcrypt_hash(const zend_string *password, zend_a
181186
zval *zcost;
182187
zend_long cost = PHP_PASSWORD_BCRYPT_COST;
183188

189+
if (zend_str_has_nul_byte(password)) {
190+
zend_value_error("Bcrypt password must not contain null character");
191+
return NULL;
192+
}
193+
184194
if (options && (zcost = zend_hash_str_find(options, "cost", sizeof("cost")-1)) != NULL) {
185195
cost = zval_get_long(zcost);
186196
}
@@ -201,7 +211,7 @@ static zend_string* php_password_bcrypt_hash(const zend_string *password, zend_a
201211
zend_string_release_ex(salt, 0);
202212

203213
/* This cast is safe, since both values are defined here in code and cannot overflow */
204-
result = php_crypt(ZSTR_VAL(password), ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
214+
result = php_crypt(ZSTR_VAL(password), (int)ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
205215
zend_string_release_ex(hash, 0);
206216

207217
if (!result) {

ext/standard/php_crypt.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
#ifndef PHP_CRYPT_H
2020
#define PHP_CRYPT_H
2121

22-
#include <stddef.h>
23-
24-
PHPAPI zend_string *php_crypt(const char *password, size_t pass_len, const char *salt, int salt_len, bool quiet);
22+
PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, bool quiet);
2523
PHP_MINIT_FUNCTION(crypt);
2624
PHP_MSHUTDOWN_FUNCTION(crypt);
2725
PHP_RINIT_FUNCTION(crypt);

ext/standard/tests/password/password_bcrypt_errors.phpt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ try {
1414
} catch (ValueError $exception) {
1515
echo $exception->getMessage() . "\n";
1616
}
17+
18+
try {
19+
var_dump(password_hash("null\0password", PASSWORD_BCRYPT));
20+
} catch (ValueError $e) {
21+
echo $e->getMessage(), "\n";
22+
}
1723
?>
1824
--EXPECT--
1925
Invalid bcrypt cost parameter specified: 3
2026
Invalid bcrypt cost parameter specified: 32
27+
Bcrypt password must not contain null character
Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,14 @@
11
--TEST--
2-
password_* handles bcrypt passwords containing null bytes
3-
--SKIPIF--
4-
<?php
5-
$password = "foo\0bar";
6-
$hash = password_hash($password, PASSWORD_BCRYPT);
7-
if (!is_string($hash) || !password_verify($password, $hash) || password_verify("foo", $hash)) {
8-
die("skip bcrypt backend truncates NUL bytes");
9-
}
10-
?>
2+
password_verify() rejects bcrypt passwords containing null bytes
113
--FILE--
124
<?php
13-
$password = "foo\0bar";
14-
$hash = password_hash($password, PASSWORD_BCRYPT);
5+
$hash = password_hash("foo", PASSWORD_BCRYPT);
156

16-
var_dump($hash !== false);
17-
var_dump(password_verify($password, $hash));
187
var_dump(password_verify("foo", $hash));
19-
var_dump(password_verify("foo\0baz", $hash));
8+
var_dump(password_verify("foo\0bar", $hash));
9+
var_dump(password_verify("\0foo", $hash));
2010
?>
2111
--EXPECT--
2212
bool(true)
23-
bool(true)
2413
bool(false)
2514
bool(false)

ext/standard/tests/strings/bug62443.phpt

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,9 @@
22
Bug #62443 Crypt SHA256/512 Segfaults With Malformed Salt
33
--FILE--
44
<?php
5-
try {
6-
crypt("foo", '$5$'.chr(0).'abc');
7-
} catch (ValueError $e) {
8-
echo $e->getMessage(), "\n";
9-
}
10-
11-
try {
12-
crypt("foo", '$6$'.chr(0).'abc');
13-
} catch (ValueError $e) {
14-
echo $e->getMessage(), "\n";
15-
}
5+
crypt("foo", '$5$'.chr(0).'abc');
6+
crypt("foo", '$6$'.chr(0).'abc');
7+
echo "OK!";
168
?>
179
--EXPECT--
18-
crypt(): Argument #2 ($salt) must not contain any null bytes
19-
crypt(): Argument #2 ($salt) must not contain any null bytes
10+
OK!

ext/standard/tests/strings/crypt_nul_bytes.phpt

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)