summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/openssl/openssl.c79
-rw-r--r--ext/openssl/tests/openssl_peer_fingerprint.phpt44
2 files changed, 104 insertions, 19 deletions
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index 9da10fc0a0..c8588e2569 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -1,4 +1,5 @@
/*
+
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
@@ -1672,6 +1673,33 @@ PHP_FUNCTION(openssl_x509_export)
}
/* }}} */
+int php_openssl_x509_fingerprint(X509 *peer, const char *method, int raw, char **out, int *out_len)
+{
+ unsigned char md[EVP_MAX_MD_SIZE];
+ const EVP_MD *mdtype;
+ int n;
+
+ if (!(mdtype = EVP_get_digestbyname(method))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
+ return 0;
+ } else if (!X509_digest(peer, mdtype, md, &n)) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not generate signature");
+ return 0;
+ }
+
+ if (raw) {
+ *out_len = n;
+ *out = estrndup(md, n);
+ } else {
+ *out_len = n * 2;
+ *out = emalloc(*out_len + 1);
+
+ make_digest_ex(*out, md, n);
+ }
+
+ return 1;
+}
+
PHP_FUNCTION(openssl_x509_fingerprint)
{
X509 *cert;
@@ -1681,9 +1709,8 @@ PHP_FUNCTION(openssl_x509_fingerprint)
char *method = "sha1";
int method_len;
- const EVP_MD *mdtype;
- unsigned char md[EVP_MAX_MD_SIZE];
- unsigned int n;
+ char *fingerprint;
+ char *fingerprint_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|sb", &zcert, &method, &method_len, &raw_output) == FAILURE) {
return;
@@ -1695,23 +1722,10 @@ PHP_FUNCTION(openssl_x509_fingerprint)
RETURN_FALSE;
}
- mdtype = EVP_get_digestbyname(method);
- if (!mdtype) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
- RETVAL_FALSE;
- } else if (!X509_digest(cert, mdtype, md, &n)) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Out of memory");
- RETVAL_FALSE;
+ if (php_openssl_x509_fingerprint(cert, method, raw_output, &fingerprint, &fingerprint_len)) {
+ RETVAL_STRINGL(fingerprint, fingerprint_len, 0);
} else {
- if (raw_output) {
- RETVAL_STRINGL(md, n, 1);
- } else {
- int digest_str_len = n * 2;
- char *digest_str = emalloc(digest_str_len + 1);
-
- make_digest_ex(digest_str, md, n);
- RETVAL_STRINGL(digest_str, digest_str_len, 0);
- }
+ RETVAL_FALSE;
}
if (certresource == -1 && cert) {
@@ -4919,6 +4933,33 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
/* if the cert passed the usual checks, apply our own local policies now */
+ if (GET_VER_OPT("peer_fingerprint") && Z_TYPE_PP(val) == IS_STRING) {
+ char *fingerprint;
+ int fingerprint_len;
+ const char *method = NULL;
+
+ switch (Z_STRLEN_PP(val)) {
+ case 32:
+ method = "md5";
+ break;
+
+ case 40:
+ method = "sha1";
+ break;
+ }
+
+ if (method && php_openssl_x509_fingerprint(peer, method, 0, &fingerprint, &fingerprint_len)) {
+ int match = strcmp(Z_STRVAL_PP(val), fingerprint) == 0;
+
+ efree(fingerprint);
+
+ if (!match) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer fingerprint `%s` not matched", Z_STRVAL_PP(val));
+ return FAILURE;
+ }
+ }
+ }
+
name = X509_get_subject_name(peer);
/* Does the common name match ? (used primarily for https://) */
diff --git a/ext/openssl/tests/openssl_peer_fingerprint.phpt b/ext/openssl/tests/openssl_peer_fingerprint.phpt
new file mode 100644
index 0000000000..a6be676dcb
--- /dev/null
+++ b/ext/openssl/tests/openssl_peer_fingerprint.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Testing peer fingerprint on connection
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!function_exists('pcntl_fork')) die("skip no fork");
+--FILE--
+<?php
+$context = stream_context_create();
+
+stream_context_set_option($context, 'ssl', 'local_cert', __DIR__ . "/bug54992.pem");
+stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
+$server = stream_socket_server('ssl://127.0.0.1:64321', $errno, $errstr,
+ STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
+
+
+$pid = pcntl_fork();
+if ($pid == -1) {
+ die('could not fork');
+} else if ($pid) {
+ $contextC = stream_context_create(
+ array(
+ 'ssl' => array(
+ 'verify_peer' => true,
+ 'cafile' => __DIR__ . '/bug54992-ca.pem',
+ 'capture_peer_cert' => true,
+ 'peer_fingerprint' => '81cafc260aa8d82956ebc6212a362ece',
+ )
+ )
+ );
+ // should be: 81cafc260aa8d82956ebc6212a362ecc
+ var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
+ STREAM_CLIENT_CONNECT, $contextC));
+} else {
+ @pcntl_wait($status);
+ @stream_socket_accept($server, 1);
+}
+--EXPECTF--
+Warning: stream_socket_client(): Peer fingerprint `81cafc260aa8d82956ebc6212a362ece` not matched in %s on line %d
+
+Warning: stream_socket_client(): Failed to enable crypto in %s on line %d
+
+Warning: stream_socket_client(): unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d
+bool(false)