summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorDaniel Lowrey <rdlowrey@php.net>2015-03-04 13:56:58 -0700
committerDaniel Lowrey <rdlowrey@php.net>2015-03-04 13:56:58 -0700
commitb5d97140c09f5aca752c8cb4bbd5a091fe5e330f (patch)
tree231ee2070af86074021554c23dfc291f71b0ba59 /ext
parentc9bd24de7adb20dad4b94e9295f6df6cdb0ce594 (diff)
parent8680fc83316be812748d3bedfc785603f263a568 (diff)
downloadphp-git-b5d97140c09f5aca752c8cb4bbd5a091fe5e330f.tar.gz
Merge branch 'tls-alpn'
* tls-alpn: Improve test to target specific issue Misc updates/cleanup Add TLS ALPN extension support in crypto client/server streams Add stream_socket_crypto_info() function Update for compatibility with newer openssl libs
Diffstat (limited to 'ext')
-rw-r--r--ext/openssl/openssl.c62
-rw-r--r--ext/openssl/tests/bug64802.phpt47
-rw-r--r--ext/openssl/xp_ssl.c481
-rw-r--r--ext/standard/basic_functions.c6
-rw-r--r--ext/standard/file.c6
-rw-r--r--ext/standard/streamsfuncs.c40
-rw-r--r--ext/standard/streamsfuncs.h1
7 files changed, 471 insertions, 172 deletions
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index 1cb69585ae..956f36171f 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -42,6 +42,10 @@
/* OpenSSL includes */
#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/dh.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/crypto.h>
@@ -3350,22 +3354,46 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
if ((req->priv_key = EVP_PKEY_new()) != NULL) {
switch(req->priv_key_type) {
case OPENSSL_KEYTYPE_RSA:
- if (EVP_PKEY_assign_RSA(req->priv_key, RSA_generate_key(req->priv_key_bits, 0x10001, NULL, NULL))) {
- return_val = req->priv_key;
+ {
+ RSA* rsaparam;
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+ /* OpenSSL 1.0.2 deprecates RSA_generate_key */
+ rsaparam = (RSA*)RSA_generate_key(req->priv_key_bits, RSA_F4, NULL, NULL);
+#else
+ {
+ BIGNUM *bne = (BIGNUM *)BN_new();
+ if (BN_set_word(bne, RSA_F4) != 1) {
+ BN_free(bne);
+ php_error_docref(NULL, E_WARNING, "failed setting exponent");
+ return NULL;
+ }
+ rsaparam = RSA_new();
+ RSA_generate_key_ex(rsaparam, req->priv_key_bits, bne, NULL);
+ BN_free(bne);
+ }
+#endif
+ if (rsaparam && EVP_PKEY_assign_RSA(req->priv_key, rsaparam)) {
+ return_val = req->priv_key;
+ }
}
break;
#if !defined(NO_DSA) && defined(HAVE_DSA_DEFAULT_METHOD)
case OPENSSL_KEYTYPE_DSA:
{
- DSA *dsapar = DSA_generate_parameters(req->priv_key_bits, NULL, 0, NULL, NULL, NULL, NULL);
- if (dsapar) {
- DSA_set_method(dsapar, DSA_get_default_method());
- if (DSA_generate_key(dsapar)) {
- if (EVP_PKEY_assign_DSA(req->priv_key, dsapar)) {
+ DSA *dsaparam = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+ dsaparam = DSA_generate_parameters(req->priv_key_bits, NULL, 0, NULL, NULL, NULL, NULL);
+#else
+ DSA_generate_parameters_ex(dsaparam, req->priv_key_bits, NULL, 0, NULL, NULL, NULL);
+#endif
+ if (dsaparam) {
+ DSA_set_method(dsaparam, DSA_get_default_method());
+ if (DSA_generate_key(dsaparam)) {
+ if (EVP_PKEY_assign_DSA(req->priv_key, dsaparam)) {
return_val = req->priv_key;
}
} else {
- DSA_free(dsapar);
+ DSA_free(dsaparam);
}
}
}
@@ -3374,17 +3402,21 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
#if !defined(NO_DH)
case OPENSSL_KEYTYPE_DH:
{
- DH *dhpar = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL);
int codes = 0;
-
- if (dhpar) {
- DH_set_method(dhpar, DH_get_default_method());
- if (DH_check(dhpar, &codes) && codes == 0 && DH_generate_key(dhpar)) {
- if (EVP_PKEY_assign_DH(req->priv_key, dhpar)) {
+ DH *dhparam = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+ dhparam = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL);
+#else
+ DH_generate_parameters_ex(dhparam, req->priv_key_bits, 2, NULL);
+#endif
+ if (dhparam) {
+ DH_set_method(dhparam, DH_get_default_method());
+ if (DH_check(dhparam, &codes) && codes == 0 && DH_generate_key(dhparam)) {
+ if (EVP_PKEY_assign_DH(req->priv_key, dhparam)) {
return_val = req->priv_key;
}
} else {
- DH_free(dhpar);
+ DH_free(dhparam);
}
}
}
diff --git a/ext/openssl/tests/bug64802.phpt b/ext/openssl/tests/bug64802.phpt
index 3a1e4fb03c..3b273998d0 100644
--- a/ext/openssl/tests/bug64802.phpt
+++ b/ext/openssl/tests/bug64802.phpt
@@ -8,50 +8,21 @@ if (!defined('OPENSSL_KEYTYPE_EC')) die("skip no EC available");
--FILE--
<?php
$cert = file_get_contents(__DIR__.'/bug64802.pem');
-$r = openssl_x509_parse($cert,$use_short_names=true);
-sort($r['subject']);
-var_dump( $r['subject'] );
+$r = openssl_x509_parse($cert,$use_short_names=false);
+var_dump($r['subject']['commonName']);
?>
--EXPECTF--
-array(11) {
+array(6) {
[0]=>
- string(14) "1550 Bryant st"
+ string(9) "www.rd.io"
[1]=>
- string(5) "94103"
+ string(8) "rdio.com"
[2]=>
- string(7) "4586007"
+ string(5) "rd.io"
[3]=>
- string(2) "CA"
+ string(12) "api.rdio.com"
[4]=>
- string(26) "COMODO EV Multi-Domain SSL"
+ string(9) "api.rd.io"
[5]=>
- string(20) "Private Organization"
- [6]=>
- string(10) "Rdio, Inc."
- [7]=>
- string(13) "San Francisco"
- [8]=>
- string(2) "US"
- [9]=>
- array(2) {
- [0]=>
- string(2) "US"
- [1]=>
- string(8) "Delaware"
- }
- [10]=>
- array(6) {
- [0]=>
- string(9) "www.rd.io"
- [1]=>
- string(8) "rdio.com"
- [2]=>
- string(5) "rd.io"
- [3]=>
- string(12) "api.rdio.com"
- [4]=>
- string(9) "api.rd.io"
- [5]=>
- string(12) "www.rdio.com"
- }
+ string(12) "www.rdio.com"
}
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index aac84b4426..cb52c0420c 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -32,10 +32,16 @@
#include "php_openssl.h"
#include "php_network.h"
#include <openssl/ssl.h>
+#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#endif
+
#ifdef PHP_WIN32
#include "win32/winutil.h"
#include "win32/time.h"
@@ -50,14 +56,34 @@
#include <sys/select.h>
#endif
+/* OpenSSL 1.0.2 removes SSLv2 support entirely*/
+#if OPENSSL_VERSION_NUMBER < 0x10002000L && !defined(OPENSSL_NO_SSL2)
+#define HAVE_SSL2 1
+#endif
+
+#ifndef OPENSSL_NO_SSL3
+#define HAVE_SSL3 1
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+#define HAVE_TLS11 1
+#define HAVE_TLS12 1
+#endif
+
#if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x0090800fL
#define HAVE_ECDH 1
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x00908070L && !defined(OPENSSL_NO_TLSEXT)
-#define HAVE_SNI 1
+#if !defined(OPENSSL_NO_TLSEXT)
+#if OPENSSL_VERSION_NUMBER >= 0x00908070L
+#define HAVE_TLS_SNI 1
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+#define HAVE_TLS_ALPN 1
+#endif
#endif
+
/* Flags for determining allowed stream crypto methods */
#define STREAM_CRYPTO_IS_CLIENT (1<<0)
#define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
@@ -74,6 +100,10 @@
/* Used for peer verification in windows */
#define PHP_X509_NAME_ENTRY_TO_UTF8(ne, i, out) ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(X509_NAME_get_entry(ne, i)))
+#ifndef OPENSSL_NO_RSA
+static RSA *tmp_rsa_cb(SSL *s, int is_export, int keylength);
+#endif
+
extern php_stream* php_openssl_get_stream_from_ssl_handle(const SSL *ssl);
extern zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, zend_bool raw);
extern int php_openssl_get_ssl_stream_data_index();
@@ -99,6 +129,14 @@ typedef struct _php_openssl_handshake_bucket_t {
unsigned should_close;
} php_openssl_handshake_bucket_t;
+#ifdef HAVE_TLS_ALPN
+/* Holds the available server ALPN protocols for negotiation */
+typedef struct _php_openssl_alpn_ctx_t {
+ unsigned char *data;
+ unsigned short len;
+} php_openssl_alpn_ctx;
+#endif
+
/* This implementation is very closely tied to the that of the native
* sockets implemented in the core.
* Don't try this technique in other extensions!
@@ -115,6 +153,9 @@ typedef struct _php_openssl_netstream_data_t {
php_openssl_handshake_bucket_t *reneg;
php_openssl_sni_cert_t *sni_certs;
unsigned sni_cert_count;
+#ifdef HAVE_TLS_ALPN
+ php_openssl_alpn_ctx *alpn_ctx;
+#endif
char *url_name;
unsigned state_set:1;
unsigned _spare:31;
@@ -897,37 +938,37 @@ static int set_local_cert(SSL_CTX *ctx, php_stream *stream) /* {{{ */
static const SSL_METHOD *php_select_crypto_method(zend_long method_value, int is_client) /* {{{ */
{
if (method_value == STREAM_CRYPTO_METHOD_SSLv2) {
-#ifndef OPENSSL_NO_SSL2
- return is_client ? SSLv2_client_method() : SSLv2_server_method();
+#ifdef HAVE_SSL2
+ return is_client ? (SSL_METHOD *)SSLv2_client_method() : (SSL_METHOD *)SSLv2_server_method();
#else
php_error_docref(NULL, E_WARNING,
- "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
+ "SSLv2 unavailable in the OpenSSL library against which PHP is linked");
return NULL;
#endif
} else if (method_value == STREAM_CRYPTO_METHOD_SSLv3) {
-#ifndef OPENSSL_NO_SSL3
+#ifdef HAVE_SSL3
return is_client ? SSLv3_client_method() : SSLv3_server_method();
#else
php_error_docref(NULL, E_WARNING,
- "SSLv3 support is not compiled into the OpenSSL library PHP is linked against");
+ "SSLv3 unavailable in the OpenSSL library against which PHP is linked");
return NULL;
#endif
} else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_0) {
return is_client ? TLSv1_client_method() : TLSv1_server_method();
} else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_1) {
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+#ifdef HAVE_TLS11
return is_client ? TLSv1_1_client_method() : TLSv1_1_server_method();
#else
php_error_docref(NULL, E_WARNING,
- "TLSv1.1 support is not compiled into the OpenSSL library PHP is linked against");
+ "TLSv1.1 unavailable in the OpenSSL library against which PHP is linked");
return NULL;
#endif
} else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_2) {
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+#ifdef HAVE_TLS12
return is_client ? TLSv1_2_client_method() : TLSv1_2_server_method();
#else
php_error_docref(NULL, E_WARNING,
- "TLSv1.2 support is not compiled into the OpenSSL library PHP is linked against");
+ "TLSv1.2 unavailable in the OpenSSL library against which PHP is linked");
return NULL;
#endif
} else {
@@ -942,26 +983,25 @@ static int php_get_crypto_method_ctx_flags(int method_flags) /* {{{ */
{
int ssl_ctx_options = SSL_OP_ALL;
-#ifndef OPENSSL_NO_SSL2
+#ifdef HAVE_SSL2
if (!(method_flags & STREAM_CRYPTO_METHOD_SSLv2)) {
ssl_ctx_options |= SSL_OP_NO_SSLv2;
}
#endif
-#ifndef OPENSSL_NO_SSL3
+#ifdef HAVE_SSL3
if (!(method_flags & STREAM_CRYPTO_METHOD_SSLv3)) {
ssl_ctx_options |= SSL_OP_NO_SSLv3;
}
#endif
-#ifndef OPENSSL_NO_TLS1
if (!(method_flags & STREAM_CRYPTO_METHOD_TLSv1_0)) {
ssl_ctx_options |= SSL_OP_NO_TLSv1;
}
-#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+#ifdef HAVE_TLS11
if (!(method_flags & STREAM_CRYPTO_METHOD_TLSv1_1)) {
ssl_ctx_options |= SSL_OP_NO_TLSv1_1;
}
-
+#endif
+#ifdef HAVE_TLS12
if (!(method_flags & STREAM_CRYPTO_METHOD_TLSv1_2)) {
ssl_ctx_options |= SSL_OP_NO_TLSv1_2;
}
@@ -1082,45 +1122,53 @@ static void init_server_reneg_limit(php_stream *stream, php_openssl_netstream_da
}
/* }}} */
-static int set_server_rsa_key(php_stream *stream, SSL_CTX *ctx) /* {{{ */
+#ifndef OPENSSL_NO_RSA
+static RSA *tmp_rsa_cb(SSL *s, int is_export, int keylength)
{
- zval *val;
- int rsa_key_size;
- RSA* rsa;
+ BIGNUM *bn = NULL;
+ static RSA *rsa_tmp = NULL;
- if ((val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "rsa_key_size")) != NULL) {
- rsa_key_size = (int) Z_LVAL_P(val);
- if ((rsa_key_size != 1) && (rsa_key_size & (rsa_key_size - 1))) {
- php_error_docref(NULL, E_WARNING, "RSA key size requires a power of 2: %d", rsa_key_size);
- rsa_key_size = 2048;
- }
- } else {
- rsa_key_size = 2048;
+ if (!rsa_tmp && ((bn = BN_new()) == NULL)) {
+ php_error_docref(NULL, E_WARNING, "allocation error generating RSA key");
}
-
- rsa = RSA_generate_key(rsa_key_size, RSA_F4, NULL, NULL);
-
- if (!SSL_CTX_set_tmp_rsa(ctx, rsa)) {
- php_error_docref(NULL, E_WARNING, "Failed setting RSA key");
- RSA_free(rsa);
- return FAILURE;
+ if (!rsa_tmp && bn) {
+ if (!BN_set_word(bn, RSA_F4) || ((rsa_tmp = RSA_new()) == NULL) ||
+ !RSA_generate_key_ex(rsa_tmp, keylength, bn, NULL)) {
+ if (rsa_tmp) {
+ RSA_free(rsa_tmp);
+ }
+ rsa_tmp = NULL;
+ }
+ BN_free(bn);
}
- RSA_free(rsa);
-
- return SUCCESS;
+ return (rsa_tmp);
}
-/* }}} */
+#endif
-static int set_server_dh_param(SSL_CTX *ctx, char *dh_path) /* {{{ */
+#ifndef OPENSSL_NO_DH
+static int set_server_dh_param(php_stream * stream, SSL_CTX *ctx) /* {{{ */
{
DH *dh;
BIO* bio;
+ zval *zdhpath;
+
+ zdhpath = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "dh_param");
+ if (zdhpath == NULL) {
+#if 0
+ /* Coming in OpenSSL 1.1 ... eventually we'll want to enable this
+ * in the absence of an explicit dh_param.
+ */
+ SSL_CTX_set_dh_auto(ctx, 1);
+#endif
+ return SUCCESS;
+ }
- bio = BIO_new_file(dh_path, "r");
+ convert_to_string_ex(zdhpath);
+ bio = BIO_new_file(Z_STRVAL_P(zdhpath), "r");
if (bio == NULL) {
- php_error_docref(NULL, E_WARNING, "Invalid dh_param file: %s", dh_path);
+ php_error_docref(NULL, E_WARNING, "invalid dh_param");
return FAILURE;
}
@@ -1128,12 +1176,12 @@ static int set_server_dh_param(SSL_CTX *ctx, char *dh_path) /* {{{ */
BIO_free(bio);
if (dh == NULL) {
- php_error_docref(NULL, E_WARNING, "Failed reading DH params from file: %s", dh_path);
+ php_error_docref(NULL, E_WARNING, "failed reading DH params");
return FAILURE;
}
if (SSL_CTX_set_tmp_dh(ctx, dh) < 0) {
- php_error_docref(NULL, E_WARNING, "DH param assignment failed");
+ php_error_docref(NULL, E_WARNING, "failed assigning DH params");
DH_free(dh);
return FAILURE;
}
@@ -1143,32 +1191,35 @@ static int set_server_dh_param(SSL_CTX *ctx, char *dh_path) /* {{{ */
return SUCCESS;
}
/* }}} */
+#endif
#ifdef HAVE_ECDH
static int set_server_ecdh_curve(php_stream *stream, SSL_CTX *ctx) /* {{{ */
{
- zval *val;
+ zval *zvcurve;
int curve_nid;
- char *curve_str;
EC_KEY *ecdh;
- if ((val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "ecdh_curve")) != NULL) {
- convert_to_string_ex(val);
- curve_str = Z_STRVAL_P(val);
- curve_nid = OBJ_sn2nid(curve_str);
+ zvcurve = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "ecdh_curve");
+ if (zvcurve == NULL) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_ecdh_auto(ctx, 1);
+ return SUCCESS;
+#else
+ curve_nid = NID_X9_62_prime256v1;
+#endif
+ } else {
+ convert_to_string_ex(zvcurve);
+ curve_nid = OBJ_sn2nid(Z_STRVAL_P(zvcurve));
if (curve_nid == NID_undef) {
- php_error_docref(NULL, E_WARNING, "Invalid ECDH curve: %s", curve_str);
+ php_error_docref(NULL, E_WARNING, "invalid ecdh_curve specified");
return FAILURE;
}
- } else {
- curve_nid = NID_X9_62_prime256v1;
}
ecdh = EC_KEY_new_by_curve_name(curve_nid);
if (ecdh == NULL) {
- php_error_docref(NULL, E_WARNING,
- "Failed generating ECDH curve");
-
+ php_error_docref(NULL, E_WARNING, "failed generating ECDH curve");
return FAILURE;
}
@@ -1182,55 +1233,35 @@ static int set_server_ecdh_curve(php_stream *stream, SSL_CTX *ctx) /* {{{ */
static int set_server_specific_opts(php_stream *stream, SSL_CTX *ctx) /* {{{ */
{
- zval *val;
+ zval *zv;
long ssl_ctx_options = SSL_CTX_get_options(ctx);
#ifdef HAVE_ECDH
- if (FAILURE == set_server_ecdh_curve(stream, ctx)) {
- return FAILURE;
- }
-#else
- val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "ecdh_curve");
- if (val != NULL) {
- php_error_docref(NULL, E_WARNING,
- "ECDH curve support not compiled into the OpenSSL lib against which PHP is linked");
-
+ if (set_server_ecdh_curve(stream, ctx) == FAILURE) {
return FAILURE;
}
#endif
- if ((val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "dh_param")) != NULL) {
- convert_to_string_ex(val);
- if (FAILURE == set_server_dh_param(ctx, Z_STRVAL_P(val))) {
- return FAILURE;
- }
- }
-
- if (FAILURE == set_server_rsa_key(stream, ctx)) {
- return FAILURE;
- }
-
- if (NULL != (val = php_stream_context_get_option(
- PHP_STREAM_CONTEXT(stream), "ssl", "honor_cipher_order")) &&
- zend_is_true(val)
- ) {
- ssl_ctx_options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+#ifndef OPENSSL_NO_RSA
+ SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
+#endif
+ /* We now use tmp_rsa_cb to generate a key of appropriate size whenever necessary */
+ if (php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "rsa_key_size") != NULL) {
+ php_error_docref(NULL, E_WARNING, "rsa_key_size context option has been removed");
}
- if (NULL != (val = php_stream_context_get_option(
- PHP_STREAM_CONTEXT(stream), "ssl", "single_dh_use")) &&
- zend_is_true(val)
- ) {
+#ifndef OPENSSL_NO_DH
+ set_server_dh_param(stream, ctx);
+ zv = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "single_dh_use");
+ if (zv != NULL && zend_is_true(zv)) {
ssl_ctx_options |= SSL_OP_SINGLE_DH_USE;
}
+#endif
-#ifdef HAVE_ECDH
- if (NULL != (val = php_stream_context_get_option(
- PHP_STREAM_CONTEXT(stream), "ssl", "single_ecdh_use")) &&
- zend_is_true(val)) {
- ssl_ctx_options |= SSL_OP_SINGLE_ECDH_USE;
+ zv = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "honor_cipher_order");
+ if (zv != NULL && zend_is_true(zv)) {
+ ssl_ctx_options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
}
-#endif
SSL_CTX_set_options(ctx, ssl_ctx_options);
@@ -1238,7 +1269,7 @@ static int set_server_specific_opts(php_stream *stream, SSL_CTX *ctx) /* {{{ */
}
/* }}} */
-#ifdef HAVE_SNI
+#ifdef HAVE_TLS_SNI
static int server_sni_callback(SSL *ssl_handle, int *al, void *arg) /* {{{ */
{
php_stream *stream;
@@ -1385,6 +1416,65 @@ static void enable_client_sni(php_stream *stream, php_openssl_netstream_data_t *
/* }}} */
#endif
+#ifdef HAVE_TLS_ALPN
+/*-
+ * parses a comma-separated list of strings into a string suitable for SSL_CTX_set_next_protos_advertised
+ * outlen: (output) set to the length of the resulting buffer on success.
+ * err: (maybe NULL) on failure, an error message line is written to this BIO.
+ * in: a NULL terminated string like "abc,def,ghi"
+ *
+ * returns: an emalloced buffer or NULL on failure.
+ */
+static unsigned char *alpn_protos_parse(unsigned short *outlen, const char *in)
+{
+ size_t len;
+ unsigned char *out;
+ size_t i, start = 0;
+
+ len = strlen(in);
+ if (len >= 65535) {
+ return NULL;
+ }
+
+ out = emalloc(strlen(in) + 1);
+ if (!out) {
+ return NULL;
+ }
+
+ for (i = 0; i <= len; ++i) {
+ if (i == len || in[i] == ',') {
+ if (i - start > 255) {
+ efree(out);
+ return NULL;
+ }
+ out[start] = i - start;
+ start = i + 1;
+ } else {
+ out[i + 1] = in[i];
+ }
+ }
+
+ *outlen = len + 1;
+
+ return out;
+}
+
+static int server_alpn_callback(SSL *ssl_handle, const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg)
+{
+ php_openssl_netstream_data_t *sslsock = arg;
+
+ if (SSL_select_next_proto
+ ((unsigned char **)out, outlen, sslsock->alpn_ctx->data, sslsock->alpn_ctx->len, in,
+ inlen) != OPENSSL_NPN_NEGOTIATED) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+#endif
+
int php_openssl_setup_crypto(php_stream *stream,
php_openssl_netstream_data_t *sslsock,
php_stream_xport_crypto_param *cparam
@@ -1394,6 +1484,7 @@ int php_openssl_setup_crypto(php_stream *stream,
int ssl_ctx_options;
int method_flags;
char *cipherlist = NULL;
+ char *alpn_protocols = NULL;
zval *val;
if (sslsock->ssl_handle) {
@@ -1478,6 +1569,38 @@ int php_openssl_setup_crypto(php_stream *stream,
return FAILURE;
}
}
+
+ GET_VER_OPT_STRING("alpn_protocols", alpn_protocols);
+ if (alpn_protocols) {
+#ifdef HAVE_TLS_ALPN
+ {
+ unsigned short alpn_len;
+ unsigned char *alpn = alpn_protos_parse(&alpn_len, alpn_protocols);
+
+ if (alpn == NULL) {
+ php_error_docref(NULL, E_WARNING, "Failed parsing comma-separated TLS ALPN protocol string");
+ SSL_CTX_free(sslsock->ctx);
+ sslsock->ctx = NULL;
+ return FAILURE;
+ }
+
+ if (sslsock->is_client) {
+ SSL_CTX_set_alpn_protos(sslsock->ctx, alpn, alpn_len);
+ } else {
+ sslsock->alpn_ctx = (php_openssl_alpn_ctx *) emalloc(sizeof(php_openssl_alpn_ctx));
+ sslsock->alpn_ctx->data = (unsigned char*)estrndup((const char*)alpn, alpn_len);
+ sslsock->alpn_ctx->len = alpn_len;
+ SSL_CTX_set_alpn_select_cb(sslsock->ctx, server_alpn_callback, sslsock);
+ }
+
+ efree(alpn);
+ }
+#else
+ php_error_docref(NULL, E_WARNING,
+ "alpn_protocols support is not compiled into the OpenSSL library against which PHP is linked");
+#endif
+ }
+
if (FAILURE == set_local_cert(sslsock->ctx, stream)) {
return FAILURE;
}
@@ -1492,6 +1615,7 @@ int php_openssl_setup_crypto(php_stream *stream,
}
sslsock->ssl_handle = SSL_new(sslsock->ctx);
+
if (sslsock->ssl_handle == NULL) {
php_error_docref(NULL, E_WARNING, "SSL handle creation failure");
SSL_CTX_free(sslsock->ctx);
@@ -1505,15 +1629,15 @@ int php_openssl_setup_crypto(php_stream *stream,
handle_ssl_error(stream, 0, 1);
}
-#ifdef HAVE_SNI
+#ifdef HAVE_TLS_SNI
/* Enable server-side SNI */
- if (sslsock->is_client == 0 && enable_server_sni(stream, sslsock) == FAILURE) {
+ if (!sslsock->is_client && enable_server_sni(stream, sslsock) == FAILURE) {
return FAILURE;
}
#endif
/* Enable server-side handshake renegotiation rate-limiting */
- if (sslsock->is_client == 0) {
+ if (!sslsock->is_client) {
init_server_reneg_limit(stream, sslsock);
}
@@ -1546,13 +1670,29 @@ static zend_array *capture_session_meta(SSL *ssl_handle) /* {{{ */
const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl_handle);
switch (proto) {
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
- case TLS1_2_VERSION: proto_str = "TLSv1.2"; break;
- case TLS1_1_VERSION: proto_str = "TLSv1.1"; break;
+#ifdef HAVE_TLS12
+ case TLS1_2_VERSION:
+ proto_str = "TLSv1.2";
+ break;
+#endif
+#ifdef HAVE_TLS11
+ case TLS1_1_VERSION:
+ proto_str = "TLSv1.1";
+ break;
+#endif
+ case TLS1_VERSION:
+ proto_str = "TLSv1";
+ break;
+#ifdef HAVE_SSL3
+ case SSL3_VERSION:
+ proto_str = "SSLv3";
+ break;
+#endif
+#ifdef HAVE_SSL2
+ case SSL2_VERSION:
+ proto_str = "SSLv2";
+ break;
#endif
- case TLS1_VERSION: proto_str = "TLSv1"; break;
- case SSL3_VERSION: proto_str = "SSLv3"; break;
- case SSL2_VERSION: proto_str = "SSLv2"; break;
default: proto_str = "UNKNOWN";
}
@@ -1566,6 +1706,105 @@ static zend_array *capture_session_meta(SSL *ssl_handle) /* {{{ */
}
/* }}} */
+static int php_openssl_crypto_info(php_stream *stream,
+ php_openssl_netstream_data_t *sslsock,
+ php_stream_xport_crypto_param *cparam
+ ) /* {{{ */
+{
+ zval *zresult;
+ const SSL_CIPHER *cipher;
+ char *cipher_name, *cipher_version, *crypto_protocol;
+ int cipher_bits;
+ const unsigned char *alpn_proto = NULL;
+ unsigned int alpn_proto_len = 0;
+ int needs_array_return;
+
+ if (!sslsock->ssl_active) {
+ php_error_docref(NULL, E_WARNING, "SSL/TLS not currently enabled for this stream");
+ return FAILURE;
+ }
+
+ zresult = cparam->inputs.zresult;
+ needs_array_return = (cparam->inputs.infotype == STREAM_CRYPTO_INFO_ALL) ? 1 : 0;
+
+ if (cparam->inputs.infotype & STREAM_CRYPTO_INFO_ALPN_PROTOCOL) {
+#ifdef HAVE_TLS_ALPN
+ SSL_get0_alpn_selected(sslsock->ssl_handle, &alpn_proto, &alpn_proto_len);
+#endif
+ if (!needs_array_return) {
+ if (alpn_proto_len > 0) {
+ ZVAL_STRINGL(zresult, (const char*)alpn_proto, alpn_proto_len);
+ }
+ return SUCCESS;
+ }
+ }
+
+ if (cparam->inputs.infotype & STREAM_CRYPTO_INFO_CIPHER) {
+ cipher = SSL_get_current_cipher(sslsock->ssl_handle);
+
+ if (cparam->inputs.infotype & STREAM_CRYPTO_INFO_CIPHER_NAME) {
+ cipher_name = (char *)SSL_CIPHER_get_name(cipher);
+ if (!needs_array_return) {
+ ZVAL_STRING(zresult, cipher_name);
+ return SUCCESS;
+ }
+ }
+
+ if (cparam->inputs.infotype & STREAM_CRYPTO_INFO_CIPHER_BITS) {
+ cipher_bits = SSL_CIPHER_get_bits(cipher, NULL);
+ if (!needs_array_return) {
+ ZVAL_LONG(zresult, cipher_bits);
+ return SUCCESS;
+ }
+ }
+
+ if (cparam->inputs.infotype & STREAM_CRYPTO_INFO_CIPHER_VERSION) {
+ cipher_version = (char *)SSL_CIPHER_get_version(cipher);
+ if (!needs_array_return) {
+ ZVAL_STRING(zresult, cipher_version);
+ return SUCCESS;
+ }
+ }
+ }
+
+ if (cparam->inputs.infotype & STREAM_CRYPTO_INFO_PROTOCOL) {
+ switch (SSL_version(sslsock->ssl_handle)) {
+#ifdef HAVE_TLS12
+ case TLS1_2_VERSION: crypto_protocol = "TLSv1.2"; break;
+#endif
+#ifdef HAVE_TLS11
+ case TLS1_1_VERSION: crypto_protocol = "TLSv1.1"; break;
+#endif
+ case TLS1_VERSION: crypto_protocol = "TLSv1"; break;
+#ifdef HAVE_SSL3
+ case SSL3_VERSION: crypto_protocol = "SSLv3"; break;
+#endif
+#ifdef HAVE_SSL2
+ case SSL2_VERSION: crypto_protocol = "SSLv2"; break;
+#endif
+ default: crypto_protocol = "UNKNOWN";
+ }
+
+ if (!needs_array_return) {
+ ZVAL_STRING(zresult, crypto_protocol);
+ return SUCCESS;
+ }
+ }
+
+ /* If we're still here we need to return an array with everything */
+ array_init(zresult);
+ add_assoc_string(zresult, "protocol", crypto_protocol);
+ add_assoc_string(zresult, "cipher_name", cipher_name);
+ add_assoc_long(zresult, "cipher_bits", cipher_bits);
+ add_assoc_string(zresult, "cipher_version", cipher_version);
+ if (alpn_proto) {
+ add_assoc_stringl(zresult, "alpn_protocol", (char *) alpn_proto, alpn_proto_len);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
static int capture_peer_certs(php_stream *stream, php_openssl_netstream_data_t *sslsock, X509 *peer_cert) /* {{{ */
{
zval *val, zcert;
@@ -1627,7 +1866,7 @@ static int php_openssl_enable_crypto(php_stream *stream,
int blocked = sslsock->s.is_blocked,
has_timeout = 0;
-#ifdef HAVE_SNI
+#ifdef HAVE_TLS_SNI
if (sslsock->is_client) {
enable_client_sni(stream, sslsock);
}
@@ -1711,10 +1950,8 @@ static int php_openssl_enable_crypto(php_stream *stream,
if (PHP_STREAM_CONTEXT(stream)) {
zval *val;
-
if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream),
- "ssl", "capture_session_meta")) &&
- zend_is_true(val)
+ "ssl", "capture_session_meta")) && zend_is_true(val)
) {
zval meta_arr;
ZVAL_ARR(&meta_arr, capture_session_meta(sslsock->ssl_handle));
@@ -1727,6 +1964,7 @@ static int php_openssl_enable_crypto(php_stream *stream,
n = 0;
} else {
n = -1;
+ /* We want to capture the peer cert even if verification fails*/
peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
if (peer_cert && PHP_STREAM_CONTEXT(stream)) {
cert_captured = capture_peer_certs(stream, sslsock, peer_cert);
@@ -2171,6 +2409,11 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val
cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam);
return PHP_STREAM_OPTION_RETURN_OK;
break;
+ case STREAM_XPORT_CRYPTO_OP_INFO:
+ return (php_openssl_crypto_info(stream, sslsock, cparam) == SUCCESS)
+ ? PHP_STREAM_OPTION_RETURN_OK
+ : PHP_STREAM_OPTION_RETURN_ERR;
+ break;
default:
/* fall through */
break;
@@ -2356,20 +2599,20 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
sslsock->enable_on_connect = 1;
sslsock->method = get_crypto_method(context, STREAM_CRYPTO_METHOD_ANY_CLIENT);
} else if (strncmp(proto, "sslv2", protolen) == 0) {
-#ifdef OPENSSL_NO_SSL2
- php_error_docref(NULL, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
- return NULL;
-#else
+#ifdef HAVE_SSL2
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
+#else
+ php_error_docref(NULL, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library against which PHP is linked");
+ return NULL;
#endif
} else if (strncmp(proto, "sslv3", protolen) == 0) {
-#ifdef OPENSSL_NO_SSL3
- php_error_docref(NULL, E_WARNING, "SSLv3 support is not compiled into the OpenSSL library PHP is linked against");
- return NULL;
-#else
+#ifdef HAVE_SSL3
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
+#else
+ php_error_docref(NULL, E_WARNING, "SSLv3 support is not compiled into the OpenSSL library against which PHP is linked");
+ return NULL;
#endif
} else if (strncmp(proto, "tls", protolen) == 0) {
sslsock->enable_on_connect = 1;
@@ -2378,19 +2621,19 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT;
} else if (strncmp(proto, "tlsv1.1", protolen) == 0) {
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+#ifdef HAVE_TLS11
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
#else
- php_error_docref(NULL, E_WARNING, "TLSv1.1 support is not compiled into the OpenSSL library PHP is linked against");
+ php_error_docref(NULL, E_WARNING, "TLSv1.1 support is not compiled into the OpenSSL library against which PHP is linked");
return NULL;
#endif
} else if (strncmp(proto, "tlsv1.2", protolen) == 0) {
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+#ifdef HAVE_TLS12
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
#else
- php_error_docref(NULL, E_WARNING, "TLSv1.2 support is not compiled into the OpenSSL library PHP is linked against");
+ php_error_docref(NULL, E_WARNING, "TLSv1.2 support is not compiled into the OpenSSL library against which PHP is linked");
return NULL;
#endif
}
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index f1c671d532..6760177a00 100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -2103,6 +2103,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_socket_enable_crypto, 0, 0, 2)
ZEND_ARG_INFO(0, sessionstream)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO(arginfo_stream_socket_crypto_info, 0)
+ ZEND_ARG_INFO(0, stream)
+ ZEND_ARG_INFO(0, infotype)
+ZEND_END_ARG_INFO()
+
#ifdef HAVE_SHUTDOWN
ZEND_BEGIN_ARG_INFO(arginfo_stream_socket_shutdown, 0)
ZEND_ARG_INFO(0, stream)
@@ -3086,6 +3091,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */
PHP_FE(stream_socket_recvfrom, arginfo_stream_socket_recvfrom)
PHP_FE(stream_socket_sendto, arginfo_stream_socket_sendto)
PHP_FE(stream_socket_enable_crypto, arginfo_stream_socket_enable_crypto)
+ PHP_FE(stream_socket_crypto_info, arginfo_stream_socket_crypto_info)
#ifdef HAVE_SHUTDOWN
PHP_FE(stream_socket_shutdown, arginfo_stream_socket_shutdown)
#endif
diff --git a/ext/standard/file.c b/ext/standard/file.c
index dae4235a99..d3ed5e12e8 100644
--- a/ext/standard/file.c
+++ b/ext/standard/file.c
@@ -231,6 +231,12 @@ PHP_MINIT_FUNCTION(file)
REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_1_SERVER", STREAM_CRYPTO_METHOD_TLSv1_1_SERVER, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_2_SERVER", STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("STREAM_CRYPTO_INFO_PROTOCOL", STREAM_CRYPTO_INFO_PROTOCOL, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("STREAM_CRYPTO_INFO_CIPHER_NAME", STREAM_CRYPTO_INFO_CIPHER_NAME, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("STREAM_CRYPTO_INFO_CIPHER_BITS", STREAM_CRYPTO_INFO_CIPHER_BITS, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("STREAM_CRYPTO_INFO_CIPHER_VERSION", STREAM_CRYPTO_INFO_CIPHER_VERSION, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("STREAM_CRYPTO_INFO_ALPN_PROTOCOL", STREAM_CRYPTO_INFO_ALPN_PROTOCOL, CONST_CS|CONST_PERSISTENT);
+
REGISTER_LONG_CONSTANT("STREAM_SHUT_RD", STREAM_SHUT_RD, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_SHUT_WR", STREAM_SHUT_WR, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_SHUT_RDWR", STREAM_SHUT_RDWR, CONST_CS|CONST_PERSISTENT);
diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c
index a86aae25f0..11dca39bc1 100644
--- a/ext/standard/streamsfuncs.c
+++ b/ext/standard/streamsfuncs.c
@@ -1485,6 +1485,46 @@ PHP_FUNCTION(stream_socket_enable_crypto)
}
/* }}} */
+/* {{{ proto int stream_socket_crypto_info(resource stream [, int infotype])
+ Retrieve information about the stream's crypto session */
+PHP_FUNCTION(stream_socket_crypto_info)
+{
+ zval *zstream = NULL;
+ php_stream *stream = NULL;
+ zend_long infotype = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zstream, &infotype) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ php_stream_from_zval(stream, zstream);
+
+ if (infotype == 0) {
+ infotype = STREAM_CRYPTO_INFO_ALL;
+ } else {
+ switch (infotype) {
+ case STREAM_CRYPTO_INFO_CIPHER_NAME:
+ case STREAM_CRYPTO_INFO_CIPHER_BITS:
+ case STREAM_CRYPTO_INFO_CIPHER_VERSION:
+ case STREAM_CRYPTO_INFO_CIPHER:
+ case STREAM_CRYPTO_INFO_PROTOCOL:
+ case STREAM_CRYPTO_INFO_ALPN_PROTOCOL:
+ case STREAM_CRYPTO_INFO_ALL:
+ break;
+ default:
+ php_error_docref(NULL, E_WARNING, "unknown crypto info type");
+ RETURN_FALSE;
+ }
+ }
+
+ if (php_stream_xport_crypto_info(stream, infotype, return_value) != PHP_STREAM_OPTION_RETURN_OK) {
+ RETURN_FALSE;
+ }
+
+ /* return_value populated by php_stream_xport_crypto_info() upon success */
+}
+/* }}} */
+
/* {{{ proto string stream_resolve_include_path(string filename)
Determine what file will be opened by calls to fopen() with a relative path */
PHP_FUNCTION(stream_resolve_include_path)
diff --git a/ext/standard/streamsfuncs.h b/ext/standard/streamsfuncs.h
index 37c6594dd2..07fe5fa572 100644
--- a/ext/standard/streamsfuncs.h
+++ b/ext/standard/streamsfuncs.h
@@ -57,6 +57,7 @@ PHP_FUNCTION(stream_filter_prepend);
PHP_FUNCTION(stream_filter_append);
PHP_FUNCTION(stream_filter_remove);
PHP_FUNCTION(stream_socket_enable_crypto);
+PHP_FUNCTION(stream_socket_crypto_info);
PHP_FUNCTION(stream_socket_shutdown);
PHP_FUNCTION(stream_resolve_include_path);
PHP_FUNCTION(stream_is_local);