Skip to content

Commit edd93f3

Browse files
committed
Support string and array for peer fingerprint matching
1 parent 69bdc5a commit edd93f3

File tree

2 files changed

+82
-25
lines changed

2 files changed

+82
-25
lines changed

ext/openssl/openssl.c

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,7 +1672,7 @@ PHP_FUNCTION(openssl_x509_export)
16721672
}
16731673
/* }}} */
16741674

1675-
int php_openssl_x509_fingerprint(X509 *peer, const char *method, int raw, char **out, int *out_len)
1675+
static int php_openssl_x509_fingerprint(X509 *peer, const char *method, int raw, char **out, int *out_len)
16761676
{
16771677
unsigned char md[EVP_MAX_MD_SIZE];
16781678
const EVP_MD *mdtype;
@@ -1699,6 +1699,61 @@ int php_openssl_x509_fingerprint(X509 *peer, const char *method, int raw, char *
16991699
return 1;
17001700
}
17011701

1702+
static int php_x509_fingerprint_cmp(X509 *peer, const char *method, const char *expected)
1703+
{
1704+
char *fingerprint;
1705+
int fingerprint_len;
1706+
int result = -1;
1707+
1708+
if (php_openssl_x509_fingerprint(peer, method, 0, &fingerprint, &fingerprint_len)) {
1709+
result = strcmp(expected, fingerprint);
1710+
efree(fingerprint);
1711+
}
1712+
1713+
return result;
1714+
}
1715+
1716+
static int php_x509_fingerprint_match(X509 *peer, zval **val)
1717+
{
1718+
if (Z_TYPE_PP(val) == IS_STRING) {
1719+
const char *method = NULL;
1720+
1721+
switch (Z_STRLEN_PP(val)) {
1722+
case 32:
1723+
method = "md5";
1724+
break;
1725+
1726+
case 40:
1727+
method = "sha1";
1728+
break;
1729+
}
1730+
1731+
return method && php_x509_fingerprint_cmp(peer, method, Z_STRVAL_PP(val)) == 0;
1732+
} else if (Z_TYPE_PP(val) == IS_ARRAY) {
1733+
HashPosition pos;
1734+
zval **current;
1735+
char *key;
1736+
uint key_len;
1737+
ulong key_index;
1738+
1739+
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(val), &pos);
1740+
zend_hash_get_current_data_ex(Z_ARRVAL_PP(val), (void **)&current, &pos) == SUCCESS;
1741+
zend_hash_move_forward_ex(Z_ARRVAL_PP(val), &pos)
1742+
) {
1743+
int key_type = zend_hash_get_current_key_ex(Z_ARRVAL_PP(val), &key, &key_len, &key_index, 0, &pos);
1744+
1745+
if (key_type == HASH_KEY_IS_STRING
1746+
&& Z_TYPE_PP(current) == IS_STRING
1747+
&& php_x509_fingerprint_cmp(peer, key, Z_STRVAL_PP(current)) != 0
1748+
) {
1749+
return 0;
1750+
}
1751+
}
1752+
return 1;
1753+
}
1754+
return 0;
1755+
}
1756+
17021757
PHP_FUNCTION(openssl_x509_fingerprint)
17031758
{
17041759
X509 *cert;
@@ -1709,7 +1764,7 @@ PHP_FUNCTION(openssl_x509_fingerprint)
17091764
int method_len;
17101765

17111766
char *fingerprint;
1712-
char *fingerprint_len;
1767+
int fingerprint_len;
17131768

17141769
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|sb", &zcert, &method, &method_len, &raw_output) == FAILURE) {
17151770
return;
@@ -4932,30 +4987,14 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
49324987

49334988
/* if the cert passed the usual checks, apply our own local policies now */
49344989

4935-
if (GET_VER_OPT("peer_fingerprint") && Z_TYPE_PP(val) == IS_STRING) {
4936-
char *fingerprint;
4937-
int fingerprint_len;
4938-
const char *method = NULL;
4939-
4940-
switch (Z_STRLEN_PP(val)) {
4941-
case 32:
4942-
method = "md5";
4943-
break;
4944-
4945-
case 40:
4946-
method = "sha1";
4947-
break;
4948-
}
4949-
4950-
if (method && php_openssl_x509_fingerprint(peer, method, 0, &fingerprint, &fingerprint_len)) {
4951-
int match = strcmp(Z_STRVAL_PP(val), fingerprint) == 0;
4952-
4953-
efree(fingerprint);
4954-
4955-
if (!match) {
4956-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer fingerprint `%s` not matched", Z_STRVAL_PP(val));
4990+
if (GET_VER_OPT("peer_fingerprint")) {
4991+
if (Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_ARRAY) {
4992+
if (!php_x509_fingerprint_match(peer, val)) {
4993+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer fingerprint doesn't match");
49574994
return FAILURE;
49584995
}
4996+
} else {
4997+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected peer fingerprint must be a string or an array");
49594998
}
49604999
}
49615000

ext/openssl/tests/openssl_peer_fingerprint.phpt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,32 @@ if ($pid == -1) {
3131
// should be: 81cafc260aa8d82956ebc6212a362ecc
3232
var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
3333
STREAM_CLIENT_CONNECT, $contextC));
34+
35+
$contextC = stream_context_create(
36+
array(
37+
'ssl' => array(
38+
'verify_peer' => true,
39+
'cafile' => __DIR__ . '/bug54992-ca.pem',
40+
'capture_peer_cert' => true,
41+
'peer_fingerprint' => array(
42+
'sha256' => '78ea579f2c3b439359dec5dac9d445108772927427c4780037e87df3799a0aa0',
43+
),
44+
)
45+
)
46+
);
47+
48+
var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
49+
STREAM_CLIENT_CONNECT, $contextC));
3450
} else {
3551
@pcntl_wait($status);
3652
@stream_socket_accept($server, 1);
53+
@stream_socket_accept($server, 1);
3754
}
3855
--EXPECTF--
39-
Warning: stream_socket_client(): Peer fingerprint `81cafc260aa8d82956ebc6212a362ece` not matched in %s on line %d
56+
Warning: stream_socket_client(): Peer fingerprint doesn't match in %s on line %d
4057

4158
Warning: stream_socket_client(): Failed to enable crypto in %s on line %d
4259

4360
Warning: stream_socket_client(): unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d
4461
bool(false)
62+
resource(9) of type (stream)

0 commit comments

Comments
 (0)