summaryrefslogtreecommitdiff
path: root/ext/openssl
diff options
context:
space:
mode:
Diffstat (limited to 'ext/openssl')
-rw-r--r--ext/openssl/openssl.c556
-rw-r--r--ext/openssl/php_openssl.h6
-rw-r--r--ext/openssl/tests/bug65729.pem28
-rw-r--r--ext/openssl/tests/bug65729.phpt54
-rw-r--r--ext/openssl/tests/openssl_peer_fingerprint.phpt62
-rw-r--r--ext/openssl/tests/openssl_spki_export.phpt62
-rw-r--r--ext/openssl/tests/openssl_spki_export_challenge.phpt105
-rw-r--r--ext/openssl/tests/openssl_spki_new.phpt77
-rw-r--r--ext/openssl/tests/openssl_spki_verify.phpt105
-rw-r--r--ext/openssl/tests/openssl_x509_fingerprint.phpt47
-rw-r--r--ext/openssl/tests/san-ca.pem15
-rw-r--r--ext/openssl/tests/san-cert.pem31
-rw-r--r--ext/openssl/tests/san_peer_matching.phpt60
-rw-r--r--ext/openssl/tests/streams_crypto_method.pem33
-rw-r--r--ext/openssl/tests/streams_crypto_method.phpt77
-rw-r--r--ext/openssl/tests/tlsv1.1_wrapper_001.phpt46
-rw-r--r--ext/openssl/tests/tlsv1.2_wrapper_002.phpt46
-rw-r--r--ext/openssl/xp_ssl.c99
18 files changed, 1472 insertions, 37 deletions
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index 90ef9b035f..8d033035e2 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -129,6 +129,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export, 0, 0, 2)
ZEND_ARG_INFO(0, notext)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_fingerprint, 0, 0, 1)
+ ZEND_ARG_INFO(0, x509)
+ ZEND_ARG_INFO(0, method)
+ ZEND_ARG_INFO(0, raw_output)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_check_private_key, 0)
ZEND_ARG_INFO(0, cert)
ZEND_ARG_INFO(0, key)
@@ -394,11 +400,35 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_random_pseudo_bytes, 0, 0, 1)
ZEND_ARG_INFO(0, length)
ZEND_ARG_INFO(1, result_is_strong)
ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_spki_new, 0, 0, 2)
+ ZEND_ARG_INFO(0, privkey)
+ ZEND_ARG_INFO(0, challenge)
+ ZEND_ARG_INFO(0, algo)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_openssl_spki_verify, 0)
+ ZEND_ARG_INFO(0, spki)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_openssl_spki_export, 0)
+ ZEND_ARG_INFO(0, spki)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_openssl_spki_export_challenge, 0)
+ ZEND_ARG_INFO(0, spki)
+ZEND_END_ARG_INFO()
/* }}} */
/* {{{ openssl_functions[]
*/
const zend_function_entry openssl_functions[] = {
+/* spki functions */
+ PHP_FE(openssl_spki_new, arginfo_openssl_spki_new)
+ PHP_FE(openssl_spki_verify, arginfo_openssl_spki_verify)
+ PHP_FE(openssl_spki_export, arginfo_openssl_spki_export)
+ PHP_FE(openssl_spki_export_challenge, arginfo_openssl_spki_export_challenge)
+
/* public/private key functions */
PHP_FE(openssl_pkey_free, arginfo_openssl_pkey_free)
PHP_FE(openssl_pkey_new, arginfo_openssl_pkey_new)
@@ -419,6 +449,7 @@ const zend_function_entry openssl_functions[] = {
PHP_FE(openssl_x509_checkpurpose, arginfo_openssl_x509_checkpurpose)
PHP_FE(openssl_x509_check_private_key, arginfo_openssl_x509_check_private_key)
PHP_FE(openssl_x509_export, arginfo_openssl_x509_export)
+ PHP_FE(openssl_x509_fingerprint, arginfo_openssl_x509_fingerprint)
PHP_FE(openssl_x509_export_to_file, arginfo_openssl_x509_export_to_file)
/* PKCS12 funcs */
@@ -791,6 +822,7 @@ static int add_oid_section(struct php_x509_request * req TSRMLS_DC) /* {{{ */
static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo);
+int openssl_spki_cleanup(const char *src, char *dest);
static int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args TSRMLS_DC) /* {{{ */
{
@@ -1161,6 +1193,10 @@ PHP_MINIT_FUNCTION(openssl)
php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory TSRMLS_CC);
#endif
php_stream_xport_register("tls", php_openssl_ssl_socket_factory TSRMLS_CC);
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+ php_stream_xport_register("tlsv1.1", php_openssl_ssl_socket_factory TSRMLS_CC);
+ php_stream_xport_register("tlsv1.2", php_openssl_ssl_socket_factory TSRMLS_CC);
+#endif
/* override the default tcp socket provider */
php_stream_xport_register("tcp", php_openssl_ssl_socket_factory TSRMLS_CC);
@@ -1199,6 +1235,10 @@ PHP_MSHUTDOWN_FUNCTION(openssl)
#endif
php_stream_xport_unregister("sslv3" TSRMLS_CC);
php_stream_xport_unregister("tls" TSRMLS_CC);
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+ php_stream_xport_unregister("tlsv1.1" TSRMLS_CC);
+ php_stream_xport_unregister("tlsv1.2" TSRMLS_CC);
+#endif
/* reinstate the default tcp handler */
php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC);
@@ -1335,6 +1375,279 @@ PHP_FUNCTION(openssl_x509_export_to_file)
}
/* }}} */
+/* {{{ proto string openssl_spki_new(mixed zpkey, string challenge [, mixed method])
+ Creates new private key (or uses existing) and creates a new spki cert
+ outputting results to var */
+PHP_FUNCTION(openssl_spki_new)
+{
+ int challenge_len;
+ char * challenge = NULL, * spkstr = NULL, * s = NULL;
+ long keyresource = -1;
+ const char *spkac = "SPKAC=";
+ long algo = OPENSSL_ALGO_MD5;
+
+ zval *method = NULL;
+ zval * zpkey = NULL;
+ EVP_PKEY * pkey = NULL;
+ NETSCAPE_SPKI *spki=NULL;
+ const EVP_MD *mdtype;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|z", &zpkey, &challenge, &challenge_len, &method) == FAILURE) {
+ return;
+ }
+ RETVAL_FALSE;
+
+ pkey = php_openssl_evp_from_zval(&zpkey, 0, challenge, 1, &keyresource TSRMLS_CC);
+
+ if (pkey == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to use supplied private key");
+ goto cleanup;
+ }
+
+ if (method != NULL) {
+ if (Z_TYPE_P(method) == IS_LONG) {
+ algo = Z_LVAL_P(method);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Algorithm must be of supported type");
+ goto cleanup;
+ }
+ }
+ mdtype = php_openssl_get_evp_md_from_algo(algo);
+
+ if (!mdtype) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
+ goto cleanup;
+ }
+
+ if ((spki = NETSCAPE_SPKI_new()) == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create new SPKAC");
+ goto cleanup;
+ }
+
+ if (challenge) {
+ ASN1_STRING_set(spki->spkac->challenge, challenge, challenge_len);
+ }
+
+ if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to embed public key");
+ goto cleanup;
+ }
+
+ if (!NETSCAPE_SPKI_sign(spki, pkey, mdtype)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to sign with specified algorithm");
+ goto cleanup;
+ }
+
+ spkstr = NETSCAPE_SPKI_b64_encode(spki);
+ if (!spkstr){
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to encode SPKAC");
+ goto cleanup;
+ }
+
+ s = emalloc(strlen(spkac) + strlen(spkstr) + 1);
+ sprintf(s, "%s%s", spkac, spkstr);
+
+ RETVAL_STRINGL(s, strlen(s), 0);
+ goto cleanup;
+
+cleanup:
+
+ if (keyresource == -1 && spki != NULL) {
+ NETSCAPE_SPKI_free(spki);
+ }
+ if (keyresource == -1 && pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+ if (keyresource == -1 && spkstr != NULL) {
+ efree(spkstr);
+ }
+
+ if (strlen(s) <= 0) {
+ RETVAL_FALSE;
+ }
+
+ if (keyresource == -1 && s != NULL) {
+ efree(s);
+ }
+}
+/* }}} */
+
+/* {{{ proto bool openssl_spki_verify(string spki)
+ Verifies spki returns boolean */
+PHP_FUNCTION(openssl_spki_verify)
+{
+ int spkstr_len, i = 0;
+ char *spkstr = NULL, * spkstr_cleaned = NULL;
+
+ EVP_PKEY *pkey = NULL;
+ NETSCAPE_SPKI *spki = NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &spkstr, &spkstr_len) == FAILURE) {
+ return;
+ }
+ RETVAL_FALSE;
+
+ if (spkstr == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to use supplied SPKAC");
+ goto cleanup;
+ }
+
+ spkstr_cleaned = emalloc(spkstr_len + 1);
+ openssl_spki_cleanup(spkstr, spkstr_cleaned);
+
+ if (strlen(spkstr_cleaned)<=0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid SPKAC");
+ goto cleanup;
+ }
+
+ spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, strlen(spkstr_cleaned));
+ if (spki == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to decode supplied SPKAC");
+ goto cleanup;
+ }
+
+ pkey = X509_PUBKEY_get(spki->spkac->pubkey);
+ if (pkey == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to acquire signed public key");
+ goto cleanup;
+ }
+
+ i = NETSCAPE_SPKI_verify(spki, pkey);
+ goto cleanup;
+
+cleanup:
+ if (spki != NULL) {
+ NETSCAPE_SPKI_free(spki);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+ if (spkstr_cleaned != NULL) {
+ efree(spkstr_cleaned);
+ }
+
+ if (i > 0) {
+ RETVAL_TRUE;
+ }
+}
+/* }}} */
+
+/* {{{ proto string openssl_spki_export(string spki)
+ Exports public key from existing spki to var */
+PHP_FUNCTION(openssl_spki_export)
+{
+ int spkstr_len;
+ char *spkstr = NULL, * spkstr_cleaned = NULL, * s = NULL;
+
+ EVP_PKEY *pkey = NULL;
+ NETSCAPE_SPKI *spki = NULL;
+ BIO *out = BIO_new(BIO_s_mem());
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &spkstr, &spkstr_len) == FAILURE) {
+ return;
+ }
+ RETVAL_FALSE;
+
+ if (spkstr == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to use supplied SPKAC");
+ goto cleanup;
+ }
+
+ spkstr_cleaned = emalloc(spkstr_len + 1);
+ openssl_spki_cleanup(spkstr, spkstr_cleaned);
+
+ spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, strlen(spkstr_cleaned));
+ if (spki == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to decode supplied SPKAC");
+ goto cleanup;
+ }
+
+ pkey = X509_PUBKEY_get(spki->spkac->pubkey);
+ if (pkey == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to acquire signed public key");
+ goto cleanup;
+ }
+
+ out = BIO_new_fp(stdout, BIO_NOCLOSE);
+ PEM_write_bio_PUBKEY(out, pkey);
+ goto cleanup;
+
+cleanup:
+
+ if (spki != NULL) {
+ NETSCAPE_SPKI_free(spki);
+ }
+ if (out != NULL) {
+ BIO_free_all(out);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+ if (spkstr_cleaned != NULL) {
+ efree(spkstr_cleaned);
+ }
+ if (s != NULL) {
+ efree(s);
+ }
+}
+/* }}} */
+
+/* {{{ proto string openssl_spki_export_challenge(string spki)
+ Exports spkac challenge from existing spki to var */
+PHP_FUNCTION(openssl_spki_export_challenge)
+{
+ int spkstr_len;
+ char *spkstr = NULL, * spkstr_cleaned = NULL;
+
+ NETSCAPE_SPKI *spki = NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &spkstr, &spkstr_len) == FAILURE) {
+ return;
+ }
+ RETVAL_FALSE;
+
+ if (spkstr == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to use supplied SPKAC");
+ goto cleanup;
+ }
+
+ spkstr_cleaned = emalloc(spkstr_len + 1);
+ openssl_spki_cleanup(spkstr, spkstr_cleaned);
+
+ spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, strlen(spkstr_cleaned));
+ if (spki == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to decode SPKAC");
+ goto cleanup;
+ }
+
+ RETVAL_STRING((char *) ASN1_STRING_data(spki->spkac->challenge), 1);
+ goto cleanup;
+
+cleanup:
+ if (spkstr_cleaned != NULL) {
+ efree(spkstr_cleaned);
+ }
+}
+/* }}} */
+
+/* {{{ strip line endings from spkac */
+int openssl_spki_cleanup(const char *src, char *dest)
+{
+ int removed=0;
+
+ while (*src) {
+ if (*src!='\n'&&*src!='\r') {
+ *dest++=*src;
+ } else {
+ ++removed;
+ }
+ ++src;
+ }
+ *dest=0;
+ return removed;
+}
+/* }}} */
+
/* {{{ proto bool openssl_x509_export(mixed x509, string &out [, bool notext = true])
Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export)
@@ -1377,6 +1690,121 @@ PHP_FUNCTION(openssl_x509_export)
}
/* }}} */
+static int php_openssl_x509_fingerprint(X509 *peer, const char *method, zend_bool raw, char **out, int *out_len TSRMLS_DC)
+{
+ unsigned char md[EVP_MAX_MD_SIZE];
+ const EVP_MD *mdtype;
+ unsigned int n;
+
+ if (!(mdtype = EVP_get_digestbyname(method))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
+ return FAILURE;
+ } else if (!X509_digest(peer, mdtype, md, &n)) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not generate signature");
+ return FAILURE;
+ }
+
+ if (raw) {
+ *out_len = n;
+ *out = estrndup((char *) md, n);
+ } else {
+ *out_len = n * 2;
+ *out = emalloc(*out_len + 1);
+
+ make_digest_ex(*out, md, n);
+ }
+
+ return SUCCESS;
+}
+
+static int php_x509_fingerprint_cmp(X509 *peer, const char *method, const char *expected TSRMLS_DC)
+{
+ char *fingerprint;
+ int fingerprint_len;
+ int result = -1;
+
+ if (php_openssl_x509_fingerprint(peer, method, 0, &fingerprint, &fingerprint_len TSRMLS_CC) == SUCCESS) {
+ result = strcmp(expected, fingerprint);
+ efree(fingerprint);
+ }
+
+ return result;
+}
+
+static zend_bool php_x509_fingerprint_match(X509 *peer, zval *val TSRMLS_DC)
+{
+ if (Z_TYPE_P(val) == IS_STRING) {
+ const char *method = NULL;
+
+ switch (Z_STRLEN_P(val)) {
+ case 32:
+ method = "md5";
+ break;
+
+ case 40:
+ method = "sha1";
+ break;
+ }
+
+ return method && php_x509_fingerprint_cmp(peer, method, Z_STRVAL_P(val) TSRMLS_CC) == 0;
+ } else if (Z_TYPE_P(val) == IS_ARRAY) {
+ HashPosition pos;
+ zval **current;
+ char *key;
+ uint key_len;
+ ulong key_index;
+
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(val), &pos);
+ zend_hash_get_current_data_ex(Z_ARRVAL_P(val), (void **)&current, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(Z_ARRVAL_P(val), &pos)
+ ) {
+ int key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(val), &key, &key_len, &key_index, 0, &pos);
+
+ if (key_type == HASH_KEY_IS_STRING
+ && Z_TYPE_PP(current) == IS_STRING
+ && php_x509_fingerprint_cmp(peer, key, Z_STRVAL_PP(current) TSRMLS_CC) != 0
+ ) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+PHP_FUNCTION(openssl_x509_fingerprint)
+{
+ X509 *cert;
+ zval **zcert;
+ long certresource;
+ zend_bool raw_output = 0;
+ char *method = "sha1";
+ int method_len;
+
+ char *fingerprint;
+ int fingerprint_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|sb", &zcert, &method, &method_len, &raw_output) == FAILURE) {
+ return;
+ }
+
+ cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
+ if (cert == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
+ RETURN_FALSE;
+ }
+
+ if (php_openssl_x509_fingerprint(cert, method, raw_output, &fingerprint, &fingerprint_len TSRMLS_CC) == SUCCESS) {
+ RETVAL_STRINGL(fingerprint, fingerprint_len, 0);
+ } else {
+ RETVAL_FALSE;
+ }
+
+ if (certresource == -1 && cert) {
+ X509_free(cert);
+ }
+}
+
/* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key)
Checks if a private key corresponds to a CERT */
PHP_FUNCTION(openssl_x509_check_private_key)
@@ -4506,14 +4934,12 @@ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */
{
php_stream *stream;
SSL *ssl;
- X509 *err_cert;
int err, depth, ret;
zval **val;
ret = preverify_ok;
/* determine the status for the current cert */
- err_cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
depth = X509_STORE_CTX_get_error_depth(ctx);
@@ -4541,12 +4967,91 @@ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */
}
/* }}} */
+static zend_bool matches_wildcard_name(const char *subjectname, const char *certname)
+{
+ char *wildcard;
+ int prefix_len, suffix_len, subject_len;
+
+ if (strcasecmp(subjectname, certname) == 0) {
+ return 1;
+ }
+
+ if (!(wildcard = strchr(certname, '*'))) {
+ return 0;
+ }
+
+ // 1) prefix, if not empty, must match subject
+ prefix_len = wildcard - certname;
+ if (prefix_len && strncasecmp(subjectname, certname, prefix_len) != 0) {
+ return 0;
+ }
+
+ suffix_len = strlen(wildcard + 1);
+ subject_len = strlen(subjectname);
+ if (suffix_len <= subject_len) {
+ /* 2) suffix must match
+ * 3) no . between prefix and suffix
+ **/
+ return strcasecmp(wildcard + 1, subjectname + subject_len - suffix_len) == 0 &&
+ memchr(subjectname + prefix_len, '.', subject_len - suffix_len - prefix_len) == NULL;
+ }
+
+ return 0;
+}
+
+static zend_bool matches_san_list(X509 *peer, const char *subject_name)
+{
+ int i;
+ zend_bool is_match = 0;
+ unsigned char *cert_name;
+
+ GENERAL_NAMES *alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, 0, 0);
+ int alt_name_count = sk_GENERAL_NAME_num(alt_names);
+
+ for (i = 0; i < alt_name_count; i++) {
+ GENERAL_NAME *san = sk_GENERAL_NAME_value(alt_names, i);
+
+ if (GEN_DNS == san->type) {
+ ASN1_STRING_to_UTF8(&cert_name, san->d.dNSName);
+ is_match = matches_wildcard_name(subject_name, (char *) cert_name);
+ OPENSSL_free(cert_name);
+ }
+
+ if (is_match) {
+ break;
+ }
+ }
+
+ return is_match;
+}
+
+static zend_bool matches_common_name(X509 *peer, const char *subject_name TSRMLS_DC)
+{
+ char buf[1024];
+ X509_NAME *cert_name;
+ zend_bool is_match = 0;
+ int cert_name_len;
+
+ cert_name = X509_get_subject_name(peer);
+ cert_name_len = X509_NAME_get_text_by_NID(cert_name, NID_commonName, buf, sizeof(buf));
+
+ if (cert_name_len == -1) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN");
+ } else if (cert_name_len != strlen(buf)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
+ } else if (matches_wildcard_name(subject_name, buf)) {
+ is_match = 1;
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", cert_name_len, buf, subject_name);
+ }
+
+ return is_match;
+}
+
int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC) /* {{{ */
{
zval **val = NULL;
char *cnmatch = NULL;
- X509_NAME *name;
- char buf[1024];
int err;
/* verification is turned off */
@@ -4577,36 +5082,25 @@ 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 */
- name = X509_get_subject_name(peer);
-
- /* Does the common name match ? (used primarily for https://) */
- GET_VER_OPT_STRING("CN_match", cnmatch);
- if (cnmatch) {
- int match = 0;
- int name_len = X509_NAME_get_text_by_NID(name, NID_commonName, buf, sizeof(buf));
-
- if (name_len == -1) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN");
- return FAILURE;
- } else if (name_len != strlen(buf)) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", name_len, buf);
- return FAILURE;
- }
-
- match = strcmp(cnmatch, buf) == 0;
- if (!match && strlen(buf) > 3 && buf[0] == '*' && buf[1] == '.') {
- /* Try wildcard */
-
- if (strchr(buf+2, '.')) {
- char *tmp = strstr(cnmatch, buf+1);
-
- match = tmp && strcmp(tmp, buf+2) && tmp == strchr(cnmatch, '.');
+ if (GET_VER_OPT("peer_fingerprint")) {
+ if (Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_ARRAY) {
+ if (!php_x509_fingerprint_match(peer, *val TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer fingerprint doesn't match");
+ return FAILURE;
}
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected peer fingerprint must be a string or an array");
}
+ }
- if (!match) {
- /* didn't match */
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", name_len, buf, cnmatch);
+ GET_VER_OPT_STRING("CN_match", cnmatch);
+
+ if (cnmatch) {
+ if (matches_san_list(peer, cnmatch)) {
+ return SUCCESS;
+ } else if (matches_common_name(peer, cnmatch TSRMLS_CC)) {
+ return SUCCESS;
+ } else {
return FAILURE;
}
}
diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h
index e6b064a277..a06e43db1c 100644
--- a/ext/openssl/php_openssl.h
+++ b/ext/openssl/php_openssl.h
@@ -66,6 +66,7 @@ PHP_FUNCTION(openssl_x509_free);
PHP_FUNCTION(openssl_x509_parse);
PHP_FUNCTION(openssl_x509_checkpurpose);
PHP_FUNCTION(openssl_x509_export);
+PHP_FUNCTION(openssl_x509_fingerprint);
PHP_FUNCTION(openssl_x509_export_to_file);
PHP_FUNCTION(openssl_x509_check_private_key);
@@ -79,6 +80,11 @@ PHP_FUNCTION(openssl_csr_export_to_file);
PHP_FUNCTION(openssl_csr_sign);
PHP_FUNCTION(openssl_csr_get_subject);
PHP_FUNCTION(openssl_csr_get_public_key);
+
+PHP_FUNCTION(openssl_spki_new);
+PHP_FUNCTION(openssl_spki_verify);
+PHP_FUNCTION(openssl_spki_export);
+PHP_FUNCTION(openssl_spki_export_challenge);
#else
#define phpext_openssl_ptr NULL
diff --git a/ext/openssl/tests/bug65729.pem b/ext/openssl/tests/bug65729.pem
new file mode 100644
index 0000000000..dbeed6efd3
--- /dev/null
+++ b/ext/openssl/tests/bug65729.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIICCTCCAXICCQDNMI29sowT7TANBgkqhkiG9w0BAQUFADBJMQswCQYDVQQGEwJT
+RzESMBAGA1UECBMJVGVzdHZpbGxlMREwDwYDVQQKEwhkYXRpYmJhdzETMBEGA1UE
+AxQKKi50ZXN0LmNvbTAeFw0xMzA5MjEwNzUyMjRaFw0xNDA5MjEwNzUyMjRaMEkx
+CzAJBgNVBAYTAlNHMRIwEAYDVQQIEwlUZXN0dmlsbGUxETAPBgNVBAoTCGRhdGli
+YmF3MRMwEQYDVQQDFAoqLnRlc3QuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQCdzVnic8K5W4SVbwVuqezcTjeqVLoQ91vVNZB0Jnsuz6q3DoK03oAd1jTe
+Vd0k+MQDbXpHoc37lA4+8z/g5Bs0UXxNx+nkbFTE7Ba2/G24caI9/cOXZPG3UViD
+rtqXKL6h5/umqRG9Dt5liF2MVP9XFAesVC7B8+Ca+PbPlQoYzwIDAQABMA0GCSqG
+SIb3DQEBBQUAA4GBAAS07u/Ke+EhEHidz6CG3Qcr+zg483JKRgZFyGz+YUKyyKKy
+fmLs7JieGJxYQjOmIpj/6X9Gnb2HjIPDnI6A+MV1emXDTnnmsgf2/lZGcthhpZn2
+rMbj9bI0iH6HwOVGtp4ZJA5fB7nj3J+gWNTCQzDDOxwX36d2LL9ua+UMnk/g
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCdzVnic8K5W4SVbwVuqezcTjeqVLoQ91vVNZB0Jnsuz6q3DoK0
+3oAd1jTeVd0k+MQDbXpHoc37lA4+8z/g5Bs0UXxNx+nkbFTE7Ba2/G24caI9/cOX
+ZPG3UViDrtqXKL6h5/umqRG9Dt5liF2MVP9XFAesVC7B8+Ca+PbPlQoYzwIDAQAB
+AoGAeyzTwKPDl5QMRejHQL57GOwlH1vLcXrjv+VzwHZZKQ0IoKM++5fCQYf29KXp
+XPahaluGW2u9sWa8R/7wGcd0Q4RtquGzsgT3+AQsIc5KfIamyOyDaRVM/ymX3fWg
+gHIU7OOzB+ihOU8sHyRIwfbk01/kmrBXLRj8E31sy3i3PIECQQDQQYE+aN7Acrdt
+yN5CaqvbkiCGjRvASlemiTzPosgOtndyp21w1gakJwKYhYDk1N6A6Qb8REMZqM/U
+wFypldV/AkEAwfq6NFuhpGL6hDA7MvlyY1KiZ0cHetPUX+PgdNqy2DA+1Sv4i7gm
+Wd/uA651K7aPXuUaf9dKtPCmZwI4M6SEsQJBALW89HTqP7niYoDEEnITdPaghxHk
+gptERUln6lGo1L1CLus3gSI/JHyMLo+7scgAnEwTD62GRKhX0Ubwt+ymfTECQAY5
+fHYnppU20+EgBxZIqOIFCc8UmWnYmE0Ha/Fz/x8u1SVUBuK84wYpSGL32yyu7ATY
+hzQo/W229zABAzqtAdECQQCUdB7IBFpPnsfv/EUBFX7X/7zAc9JpACmu9It5ju8C
+KIsMuz/02D+TQoJNjdAngBM+4AJDIaGFgTMIfaDMh5L7
+-----END RSA PRIVATE KEY-----
diff --git a/ext/openssl/tests/bug65729.phpt b/ext/openssl/tests/bug65729.phpt
new file mode 100644
index 0000000000..c0ee4443eb
--- /dev/null
+++ b/ext/openssl/tests/bug65729.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Bug #65729: CN_match gives false positive when wildcard is used
+--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__ . "/bug65729.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);
+
+$expected_names = array('foo.test.com.sg', 'foo.test.com', 'FOO.TEST.COM', 'foo.bar.test.com');
+
+$pid = pcntl_fork();
+if ($pid == -1) {
+ die('could not fork');
+} else if ($pid) {
+ foreach ($expected_names as $expected_name) {
+ $contextC = stream_context_create(array(
+ 'ssl' => array(
+ 'verify_peer' => true,
+ 'allow_self_signed' => true,
+ 'CN_match' => $expected_name,
+ )
+ ));
+ var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
+ STREAM_CLIENT_CONNECT, $contextC));
+ }
+} else {
+ @pcntl_wait($status);
+ foreach ($expected_names as $name) {
+ @stream_socket_accept($server, 1);
+ }
+}
+--EXPECTF--
+Warning: stream_socket_client(): Peer certificate CN=`*.test.com' did not match expected CN=`foo.test.com.sg' 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)
+resource(%d) of type (stream)
+resource(%d) of type (stream)
+
+Warning: stream_socket_client(): Peer certificate CN=`*.test.com' did not match expected CN=`foo.bar.test.com' 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)
diff --git a/ext/openssl/tests/openssl_peer_fingerprint.phpt b/ext/openssl/tests/openssl_peer_fingerprint.phpt
new file mode 100644
index 0000000000..2960dffae5
--- /dev/null
+++ b/ext/openssl/tests/openssl_peer_fingerprint.phpt
@@ -0,0 +1,62 @@
+--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));
+
+ $contextC = stream_context_create(
+ array(
+ 'ssl' => array(
+ 'verify_peer' => true,
+ 'cafile' => __DIR__ . '/bug54992-ca.pem',
+ 'capture_peer_cert' => true,
+ 'peer_fingerprint' => array(
+ 'sha256' => '78ea579f2c3b439359dec5dac9d445108772927427c4780037e87df3799a0aa0',
+ ),
+ )
+ )
+ );
+
+ 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);
+ @stream_socket_accept($server, 1);
+}
+--EXPECTF--
+Warning: stream_socket_client(): Peer fingerprint doesn't match 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)
+resource(9) of type (stream)
diff --git a/ext/openssl/tests/openssl_spki_export.phpt b/ext/openssl/tests/openssl_spki_export.phpt
new file mode 100644
index 0000000000..59332f70a5
--- /dev/null
+++ b/ext/openssl/tests/openssl_spki_export.phpt
@@ -0,0 +1,62 @@
+--TEST--
+Testing openssl_spki_export()
+Creates SPKAC for all available key sizes & signature algorithms and exports public key
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!@openssl_pkey_new()) die("skip cannot create private key");
+?>
+--FILE--
+<?php
+
+/* array of private key sizes to test */
+$ksize = array('1024'=>1024,
+ '2048'=>2048,
+ '4096'=>4096);
+
+/* array of available hashings to test */
+$algo = array('md4'=>OPENSSL_ALGO_MD4,
+ 'md5'=>OPENSSL_ALGO_MD5,
+ 'sha1'=>OPENSSL_ALGO_SHA1,
+ 'sha224'=>OPENSSL_ALGO_SHA224,
+ 'sha256'=>OPENSSL_ALGO_SHA256,
+ 'sha384'=>OPENSSL_ALGO_SHA384,
+ 'sha512'=>OPENSSL_ALGO_SHA512,
+ 'rmd160'=>OPENSSL_ALGO_RMD160);
+
+/* loop over key sizes for test */
+foreach($ksize as $k => $v) {
+
+ /* generate new private key of specified size to use for tests */
+ $pkey = openssl_pkey_new(array('digest_alg' => 'sha512',
+ 'private_key_type' => OPENSSL_KEYTYPE_RSA,
+ 'private_key_bits' => $v));
+ openssl_pkey_export($pkey, $pass);
+
+ /* loop to create and verify results */
+ foreach($algo as $key => $value) {
+ $spkac = openssl_spki_new($pkey, _uuid(), $value);
+ echo openssl_spki_export(preg_replace('/SPKAC=/', '', $spkac));
+ }
+ openssl_free_key($pkey);
+}
+
+/* generate a random challenge */
+function _uuid()
+{
+ return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000,
+ mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff));
+}
+
+?>
+--EXPECTREGEX--
+\-\-\-\-\-BEGIN PUBLIC KEY\-\-\-\-\-.*\-\-\-\-\-END PUBLIC KEY\-\-\-\-\-
+\-\-\-\-\-BEGIN PUBLIC KEY\-\-\-\-\-.*\-\-\-\-\-END PUBLIC KEY\-\-\-\-\-
+\-\-\-\-\-BEGIN PUBLIC KEY\-\-\-\-\-.*\-\-\-\-\-END PUBLIC KEY\-\-\-\-\-
+\-\-\-\-\-BEGIN PUBLIC KEY\-\-\-\-\-.*\-\-\-\-\-END PUBLIC KEY\-\-\-\-\-
+\-\-\-\-\-BEGIN PUBLIC KEY\-\-\-\-\-.*\-\-\-\-\-END PUBLIC KEY\-\-\-\-\-
+\-\-\-\-\-BEGIN PUBLIC KEY\-\-\-\-\-.*\-\-\-\-\-END PUBLIC KEY\-\-\-\-\-
+\-\-\-\-\-BEGIN PUBLIC KEY\-\-\-\-\-.*\-\-\-\-\-END PUBLIC KEY\-\-\-\-\-
+\-\-\-\-\-BEGIN PUBLIC KEY\-\-\-\-\-.*\-\-\-\-\-END PUBLIC KEY\-\-\-\-\-
diff --git a/ext/openssl/tests/openssl_spki_export_challenge.phpt b/ext/openssl/tests/openssl_spki_export_challenge.phpt
new file mode 100644
index 0000000000..71ef62edd5
--- /dev/null
+++ b/ext/openssl/tests/openssl_spki_export_challenge.phpt
@@ -0,0 +1,105 @@
+--TEST--
+Testing openssl_spki_export_challenge()
+Creates SPKAC for all available key sizes & signature algorithms and exports challenge
+--INI--
+error_reporting=0
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!@openssl_pkey_new()) die("skip cannot create private key");
+?>
+--FILE--
+<?php
+
+/* array of private key sizes to test */
+$ksize = array('1024'=>1024,
+ '2048'=>2048,
+ '4096'=>4096);
+
+/* array of available hashings to test */
+$algo = array('md4'=>OPENSSL_ALGO_MD4,
+ 'md5'=>OPENSSL_ALGO_MD5,
+ 'sha1'=>OPENSSL_ALGO_SHA1,
+ 'sha224'=>OPENSSL_ALGO_SHA224,
+ 'sha256'=>OPENSSL_ALGO_SHA256,
+ 'sha384'=>OPENSSL_ALGO_SHA384,
+ 'sha512'=>OPENSSL_ALGO_SHA512,
+ 'rmd160'=>OPENSSL_ALGO_RMD160);
+
+/* loop over key sizes for test */
+foreach($ksize as $k => $v) {
+
+ /* generate new private key of specified size to use for tests */
+ $pkey = openssl_pkey_new(array('digest_alg' => 'sha512',
+ 'private_key_type' => OPENSSL_KEYTYPE_RSA,
+ 'private_key_bits' => $v));
+ openssl_pkey_export($pkey, $pass);
+
+ /* loop to create and verify results */
+ foreach($algo as $key => $value) {
+ $spkac = openssl_spki_new($pkey, _uuid(), $value);
+ var_dump(openssl_spki_export_challenge(preg_replace('/SPKAC=/', '', $spkac)));
+ var_dump(openssl_spki_export_challenge($spkac.'Make it fail'));
+ }
+ openssl_free_key($pkey);
+}
+
+/* generate a random challenge */
+function _uuid()
+{
+ return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000,
+ mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff));
+}
+
+?>
+--EXPECTREGEX--
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
+string\(36\) \"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}\"
+bool\(false\)
diff --git a/ext/openssl/tests/openssl_spki_new.phpt b/ext/openssl/tests/openssl_spki_new.phpt
new file mode 100644
index 0000000000..e40f9bf28e
--- /dev/null
+++ b/ext/openssl/tests/openssl_spki_new.phpt
@@ -0,0 +1,77 @@
+--TEST--
+Testing openssl_spki_new()
+Tests SPKAC for all available private key sizes & hashing algorithms
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!@openssl_pkey_new()) die("skip cannot create private key");
+?>
+--FILE--
+<?php
+
+/* array of private key sizes to test */
+$ksize = array('1024'=>1024,
+ '2048'=>2048,
+ '4096'=>4096);
+
+/* array of available hashings to test */
+$algo = array('md4'=>OPENSSL_ALGO_MD4,
+ 'md5'=>OPENSSL_ALGO_MD5,
+ 'sha1'=>OPENSSL_ALGO_SHA1,
+ 'sha224'=>OPENSSL_ALGO_SHA224,
+ 'sha256'=>OPENSSL_ALGO_SHA256,
+ 'sha384'=>OPENSSL_ALGO_SHA384,
+ 'sha512'=>OPENSSL_ALGO_SHA512,
+ 'rmd160'=>OPENSSL_ALGO_RMD160);
+
+/* loop over key sizes for test */
+foreach($ksize as $k => $v) {
+
+ /* generate new private key of specified size to use for tests */
+ $pkey = openssl_pkey_new(array('digest_alg' => 'sha512',
+ 'private_key_type' => OPENSSL_KEYTYPE_RSA,
+ 'private_key_bits' => $v));
+ openssl_pkey_export($pkey, $pass);
+
+ /* loop to create and verify results */
+ foreach($algo as $key => $value) {
+ var_dump(openssl_spki_new($pkey, _uuid(), $value));
+ }
+ openssl_free_key($pkey);
+}
+
+/* generate a random challenge */
+function _uuid()
+{
+ return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000,
+ mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff));
+}
+
+?>
+--EXPECTF--
+string(478) "%s"
+string(478) "%s"
+string(478) "%s"
+string(478) "%s"
+string(478) "%s"
+string(478) "%s"
+string(478) "%s"
+string(474) "%s"
+string(830) "%s"
+string(830) "%s"
+string(830) "%s"
+string(830) "%s"
+string(830) "%s"
+string(830) "%s"
+string(830) "%s"
+string(826) "%s"
+string(1510) "%s"
+string(1510) "%s"
+string(1510) "%s"
+string(1510) "%s"
+string(1510) "%s"
+string(1510) "%s"
+string(1510) "%s"
+string(1506) "%s"
diff --git a/ext/openssl/tests/openssl_spki_verify.phpt b/ext/openssl/tests/openssl_spki_verify.phpt
new file mode 100644
index 0000000000..1ee573fd3f
--- /dev/null
+++ b/ext/openssl/tests/openssl_spki_verify.phpt
@@ -0,0 +1,105 @@
+--TEST--
+Testing openssl_spki_verify()
+Creates SPKAC for all available key sizes & signature algorithms and tests for valid signature
+--INI--
+error_reporting=0
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!@openssl_pkey_new()) die("skip cannot create private key");
+?>
+--FILE--
+<?php
+
+/* array of private key sizes to test */
+$ksize = array('1024'=>1024,
+ '2048'=>2048,
+ '4096'=>4096);
+
+/* array of available hashings to test */
+$algo = array('md4'=>OPENSSL_ALGO_MD4,
+ 'md5'=>OPENSSL_ALGO_MD5,
+ 'sha1'=>OPENSSL_ALGO_SHA1,
+ 'sha224'=>OPENSSL_ALGO_SHA224,
+ 'sha256'=>OPENSSL_ALGO_SHA256,
+ 'sha384'=>OPENSSL_ALGO_SHA384,
+ 'sha512'=>OPENSSL_ALGO_SHA512,
+ 'rmd160'=>OPENSSL_ALGO_RMD160);
+
+/* loop over key sizes for test */
+foreach($ksize as $k => $v) {
+
+ /* generate new private key of specified size to use for tests */
+ $pkey = openssl_pkey_new(array('digest_alg' => 'sha512',
+ 'private_key_type' => OPENSSL_KEYTYPE_RSA,
+ 'private_key_bits' => $v));
+ openssl_pkey_export($pkey, $pass);
+
+ /* loop to create and verify results */
+ foreach($algo as $key => $value) {
+ $spkac = openssl_spki_new($pkey, _uuid(), $value);
+ var_dump(openssl_spki_verify(preg_replace('/SPKAC=/', '', $spkac)));
+ var_dump(openssl_spki_verify($spkac.'Make it fail'));
+ }
+ openssl_free_key($pkey);
+}
+
+/* generate a random challenge */
+function _uuid()
+{
+ return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000,
+ mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff));
+}
+
+?>
+--EXPECT--
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false) \ No newline at end of file
diff --git a/ext/openssl/tests/openssl_x509_fingerprint.phpt b/ext/openssl/tests/openssl_x509_fingerprint.phpt
new file mode 100644
index 0000000000..6cd464a894
--- /dev/null
+++ b/ext/openssl/tests/openssl_x509_fingerprint.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Testing openssl_x509_fingerprint()
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+?>
+--FILE--
+<?php
+
+$cert = "file://" . dirname(__FILE__) . "/cert.crt";
+
+echo "** Testing with no parameters **\n";
+var_dump(openssl_x509_fingerprint());
+
+echo "** Testing default functionality **\n";
+var_dump(openssl_x509_fingerprint($cert));
+
+echo "** Testing hash method md5 **\n";
+var_dump(openssl_x509_fingerprint($cert, 'md5'));
+
+echo "**Testing raw output md5 **\n";
+var_dump(bin2hex(openssl_x509_fingerprint($cert, 'md5', true)));
+
+echo "** Testing bad certification **\n";
+var_dump(openssl_x509_fingerprint('123'));
+echo "** Testing bad hash method **\n";
+var_dump(openssl_x509_fingerprint($cert, 'xx45'));
+--EXPECTF--
+** Testing with no parameters **
+
+Warning: openssl_x509_fingerprint() expects at least 1 parameter, 0 given in %s on line %d
+NULL
+** Testing default functionality **
+string(40) "6e6fd1ea10a5a23071d61c728ee9b40df6dbc33c"
+** Testing hash method md5 **
+string(32) "ac77008e172897e06c0b065294487a67"
+**Testing raw output md5 **
+string(32) "ac77008e172897e06c0b065294487a67"
+** Testing bad certification **
+
+Warning: openssl_x509_fingerprint(): cannot get cert from parameter 1 in %s on line %d
+bool(false)
+** Testing bad hash method **
+
+Warning: openssl_x509_fingerprint(): Unknown signature algorithm in %s on line %d
+bool(false)
+
diff --git a/ext/openssl/tests/san-ca.pem b/ext/openssl/tests/san-ca.pem
new file mode 100644
index 0000000000..88682ba2dc
--- /dev/null
+++ b/ext/openssl/tests/san-ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICYTCCAcqgAwIBAgIJAIaqxtY5dwjtMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UEBxMLTWlubmVhcG9saXMxITAfBgNV
+BAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDAeFw0xMzA5MjQwODA1NTFaFw0y
+MTEyMTEwODA1NTFaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UE
+BxMLTWlubmVhcG9saXMxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl
+ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsFGqfbU/8D+KjroQl4XMyt9m
+dcSP7iZtqphOu9nVZxYAAqfaqj8FnC/pwYV3TU6ZHndLTQAllwYT3sQBQPPGmZQ9
+clSIMEL003t3pi4ZVXkttG6Vvr+Z9PBcHhlKLQ7WMHnn4qctllWXTSoyTQpkETF3
+Fc3mrG5G37BhoUno7NECAwEAAaM9MDswOQYDVR0RBDIwMIILZXhhbXBsZS5vcmeC
+D3d3dy5leGFtcGxlLm9yZ4IQdGVzdC5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQUF
+AAOBgQBf/FZhzheIcQJ+dyTk8xQ/nJLvpmBhbd1LNtfwk/MsC9UHsz4QXs9sBw1k
+rH0FjoqgM6avj7zKHJFTj6q7Rd+OX5V4HynYPhX67sWbN3KWEHffL98nGGd/bo3X
+pSjNk5vnyKYiwdUUe11Ac9csh0HcSBbhOYjy0T/i9AlQcKbuCg==
+-----END CERTIFICATE-----
diff --git a/ext/openssl/tests/san-cert.pem b/ext/openssl/tests/san-cert.pem
new file mode 100644
index 0000000000..923d490e72
--- /dev/null
+++ b/ext/openssl/tests/san-cert.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIICYTCCAcqgAwIBAgIJAIaqxtY5dwjtMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UEBxMLTWlubmVhcG9saXMxITAfBgNV
+BAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDAeFw0xMzA5MjQwODA1NTFaFw0y
+MTEyMTEwODA1NTFaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UE
+BxMLTWlubmVhcG9saXMxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl
+ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsFGqfbU/8D+KjroQl4XMyt9m
+dcSP7iZtqphOu9nVZxYAAqfaqj8FnC/pwYV3TU6ZHndLTQAllwYT3sQBQPPGmZQ9
+clSIMEL003t3pi4ZVXkttG6Vvr+Z9PBcHhlKLQ7WMHnn4qctllWXTSoyTQpkETF3
+Fc3mrG5G37BhoUno7NECAwEAAaM9MDswOQYDVR0RBDIwMIILZXhhbXBsZS5vcmeC
+D3d3dy5leGFtcGxlLm9yZ4IQdGVzdC5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQUF
+AAOBgQBf/FZhzheIcQJ+dyTk8xQ/nJLvpmBhbd1LNtfwk/MsC9UHsz4QXs9sBw1k
+rH0FjoqgM6avj7zKHJFTj6q7Rd+OX5V4HynYPhX67sWbN3KWEHffL98nGGd/bo3X
+pSjNk5vnyKYiwdUUe11Ac9csh0HcSBbhOYjy0T/i9AlQcKbuCg==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALBRqn21P/A/io66
+EJeFzMrfZnXEj+4mbaqYTrvZ1WcWAAKn2qo/BZwv6cGFd01OmR53S00AJZcGE97E
+AUDzxpmUPXJUiDBC9NN7d6YuGVV5LbRulb6/mfTwXB4ZSi0O1jB55+KnLZZVl00q
+Mk0KZBExdxXN5qxuRt+wYaFJ6OzRAgMBAAECgYB11e5iWvqjPmQEZRdnnJU0VD8u
+n7ItT+Nk6qtb4gY8Abj6DWIW+01th5vqqJ8FvGyartFVYa69kuM+srG/zevAZWeu
+fGZtwiwZR4DRSyRcPp4rnNiksK3dkAZA6UewmRDPv8uyHJlXc5i+Ft1ILJ5Q5jgn
+UkC4z3EJP5Se9KZywQJBAOO4lRq42wLsYr2SDrQDSs4leie3FKc2bgvjF7Djosh1
+ZYbf55F5b9w1zgnccmni2HkqOnyFu4SKarmXyCsYxrkCQQDGNvnUh7/zZswrdWZ/
+PMp9zVDTh/5Oc2B4ByNLw1ERDwYhjchKgPRlQvn4cp3Pwf3UYPQ/8XGXzzEJey3A
+r0rZAkBf/tDEOgcBPXsGZQrTscuYCU5sbY5ESvqrAilbhSp7DJom+D5bIfEYyIm5
+uHd20Yzlzvpmwc1huyPwZt6X5FLpAkATDReoGMAXSesXxjnqwtIHk2NQYYLM0YQV
+JUJ8NrKk/Bevw+vbVVeoH+7ctU97t36JGiR/vNoZKD3jVmaIXZDJAkEA4wJbwzIo
+L32mu9VmZa7wjmfkraQEmXTPaA5D9lNC0AwRTgkj+x2Qe1vawNblNK9PPLBDdplQ
+L//53ADq/wv5rA==
+-----END PRIVATE KEY-----
diff --git a/ext/openssl/tests/san_peer_matching.phpt b/ext/openssl/tests/san_peer_matching.phpt
new file mode 100644
index 0000000000..4e6531d6cc
--- /dev/null
+++ b/ext/openssl/tests/san_peer_matching.phpt
@@ -0,0 +1,60 @@
+--TEST--
+Peer verification matches SAN names
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!function_exists('pcntl_fork')) die("skip no fork");
+--FILE--
+<?php
+$context = stream_context_create(array(
+ 'ssl' => array(
+ 'local_cert' => __DIR__ . '/san-cert.pem',
+ '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__ . '/san-ca.pem',
+ 'CN_match' => 'example.org',
+ )
+ )
+ );
+ var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
+ STREAM_CLIENT_CONNECT, $contextC));
+
+ $contextC = stream_context_create(array(
+ 'ssl' => array(
+ 'verify_peer' => true,
+ 'cafile' => __DIR__ . '/san-ca.pem',
+ 'CN_match' => 'moar.example.org',
+ )
+ ));
+
+ 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);
+ @stream_socket_accept($server, 1);
+}
+--EXPECTF--
+resource(%d) of type (stream)
+
+Warning: stream_socket_client(): Unable to locate peer certificate CN 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)
diff --git a/ext/openssl/tests/streams_crypto_method.pem b/ext/openssl/tests/streams_crypto_method.pem
new file mode 100644
index 0000000000..9d754d460d
--- /dev/null
+++ b/ext/openssl/tests/streams_crypto_method.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIC5jCCAk+gAwIBAgIBADANBgkqhkiG9w0BAQQFADBcMQswCQYDVQQGEwJBVTET
+MBEGA1UECBMKUXVlZW5zbGFuZDEaMBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQx
+HDAaBgNVBAMTE1Rlc3QgUENBICgxMDI0IGJpdCkwHhcNOTkxMjAyMjEzNTQ4WhcN
+MDUwNzExMjEzNTQ4WjBcMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFu
+ZDEaMBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxHDAaBgNVBAMTE1Rlc3QgUENB
+ICgxMDI0IGJpdCkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ2haT/f5Zwy
+V+MiuSDjSR62adBoSiBB7Usty44lXqsp9RICw+DCCxpsn/CfxPEDXLLd4olsWXc6
+JRcxGynbYmnzk+Z6aIPPJQhK3CTvaqGnWKZsA1m+WaUIUqJCuNTK4N+7hMAGaf6S
+S3e9HVgEQ4a34gXJ7VQFVIBNV1EnZRWHAgMBAAGjgbcwgbQwHQYDVR0OBBYEFE0R
+aEcrj18q1dw+G6nJbsTWR213MIGEBgNVHSMEfTB7gBRNEWhHK49fKtXcPhupyW7E
+1kdtd6FgpF4wXDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClF1ZWVuc2xhbmQxGjAY
+BgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRwwGgYDVQQDExNUZXN0IFBDQSAoMTAy
+NCBiaXQpggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAUa8B3pho
++Mvxeq9HsEzJxHIFQla05S5J/e/V+DQTYoKiRFchKPrDAdrzYSEvP3h4QJEtsNqQ
+JfOxg5M42uLFq7aPGWkF6ZZqZsYS+zA9IVT14g7gNA6Ne+5QtJqQtH9HA24st0T0
+Tga/lZ9M2ovImovaxSL/kRHbpCWcqWVxpOw=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCdoWk/3+WcMlfjIrkg40ketmnQaEogQe1LLcuOJV6rKfUSAsPg
+wgsabJ/wn8TxA1yy3eKJbFl3OiUXMRsp22Jp85PmemiDzyUIStwk72qhp1imbANZ
+vlmlCFKiQrjUyuDfu4TABmn+kkt3vR1YBEOGt+IFye1UBVSATVdRJ2UVhwIDAQAB
+AoGAba4fTtuap5l7/8ZsbE7Z1O32KJY4ZcOZukLOLUUhXxXduT+FTgGWujc0/rgc
+z9qYCLlNZHOouMYTgtSfYvuMuLZ11VIt0GYH+nRioLShE59Yy+zCRyC+gPigS1kz
+xvo14AsOIPYV14Tk/SsHyq6E0eTk7VzaIE197giiINUERPECQQDSKmtPTh/lRKw7
+HSZSM0I1mFWn/1zqrAbontRQY5w98QWIOe5qmzYyFbPXYT3d9BzlsMyhgiRNoBbD
+yvohSHXJAkEAwAHx6ezAZeWWzD5yXD36nyjpkVCw7Tk7TSmOceLJMWt1QcrCfqlS
+xA5jjpQ6Z8suU5DdtWAryM2sAir1WisYzwJAd6Zcx56jvAQ3xcPXsE6scBTVFzrj
+7FqZ6E+cclPzfLQ+QQsyOBE7bpI6e/FJppY26XGZXo3YGzV8IGXrt40oOQJALETG
+h86EFXo3qGOFbmsDy4pdP5nBERCu8X1xUCSfintiD4c2DInxgS5oGclnJeMcjTvL
+QjQoJCX3UJCi/OUO1QJBAKgcDHWjMvt+l1pjJBsSEZ0HX9AAIIVx0RQmbFGS+F2Q
+hhu5l77WnnZOQ9vvhV5u7NPCUF9nhU3jh60qWWO8mkc=
+-----END RSA PRIVATE KEY-----
diff --git a/ext/openssl/tests/streams_crypto_method.phpt b/ext/openssl/tests/streams_crypto_method.phpt
new file mode 100644
index 0000000000..97a6e9ee8b
--- /dev/null
+++ b/ext/openssl/tests/streams_crypto_method.phpt
@@ -0,0 +1,77 @@
+--TEST--
+Specific crypto method for ssl:// transports.
+--SKIPIF--
+<?php
+if (!extension_loaded('openssl')) die('skip, openssl required');
+if (!extension_loaded('pcntl')) die('skip, pcntl required');
+?>
+--FILE--
+<?php
+function client($port, $method) {
+ $ctx = stream_context_create();
+ stream_context_set_option($ctx, 'ssl', 'crypto_method', $method);
+
+ $fp = @fopen('https://127.0.0.1:' . $port . '/', 'r', false, $ctx);
+ if ($fp) {
+ fpassthru($fp);
+ fclose($fp);
+ }
+}
+
+function server($port, $transport) {
+ $context = stream_context_create();
+
+ stream_context_set_option($context, 'ssl', 'local_cert', dirname(__FILE__) . '/streams_crypto_method.pem');
+ stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
+ stream_context_set_option($context, 'ssl', 'verify_peer', false);
+
+ $server = stream_socket_server($transport . '127.0.0.1:' . $port, $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
+
+ $client = @stream_socket_accept($server);
+
+ if ($client) {
+ $in = '';
+ while (!preg_match('/\r?\n\r?\n/', $in)) {
+ $in .= fread($client, 2048);
+ }
+
+ $response = <<<EOS
+HTTP/1.1 200 OK
+Content-Type: text/plain
+Content-Length: 13
+Connection: close
+
+Hello World!
+
+EOS;
+
+ fwrite($client, $response);
+ fclose($client);
+ exit();
+ }
+}
+
+$port1 = rand(15000, 16000);
+$port2 = rand(16001, 17000);
+
+$pid1 = pcntl_fork();
+$pid2 = pcntl_fork();
+
+if ($pid1 == 0 && $pid2 != 0) {
+ server($port1, 'sslv3://');
+ exit;
+}
+
+if ($pid1 != 0 && $pid2 == 0) {
+ server($port2, 'sslv3://');
+ exit;
+}
+
+client($port1, STREAM_CRYPTO_METHOD_SSLv3_CLIENT);
+client($port2, STREAM_CRYPTO_METHOD_SSLv2_CLIENT);
+
+pcntl_waitpid($pid1, $status);
+pcntl_waitpid($pid2, $status);
+?>
+--EXPECTF--
+Hello World!
diff --git a/ext/openssl/tests/tlsv1.1_wrapper_001.phpt b/ext/openssl/tests/tlsv1.1_wrapper_001.phpt
new file mode 100644
index 0000000000..56211f0b96
--- /dev/null
+++ b/ext/openssl/tests/tlsv1.1_wrapper_001.phpt
@@ -0,0 +1,46 @@
+--TEST--
+tlsv1.1 stream wrapper
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (OPENSSL_VERSION_NUMBER < 0x10001001) die("skip OpenSSL 1.0.1 required");
+if (!function_exists('pcntl_fork')) die("skip no fork");
+--FILE--
+<?php
+$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
+$ctx = stream_context_create(array('ssl' => array(
+ 'local_cert' => __DIR__ . '/streams_crypto_method.pem',
+)));
+
+$server = stream_socket_server('tlsv1.1://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
+var_dump($server);
+
+$pid = pcntl_fork();
+if ($pid == -1) {
+ die('could not fork');
+} elseif ($pid) {
+ $flags = STREAM_CLIENT_CONNECT;
+ $ctx = stream_context_create(array('ssl' => array(
+ 'verify_peer' => false
+ )));
+
+ $client = stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
+ var_dump($client);
+
+ $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
+ var_dump($client);
+
+ $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
+ var_dump($client);
+
+} else {
+ @pcntl_wait($status);
+ for ($i=0; $i < 3; $i++) {
+ @stream_socket_accept($server, 1);
+ }
+}
+--EXPECTF--
+resource(%d) of type (stream)
+resource(%d) of type (stream)
+bool(false)
+bool(false)
diff --git a/ext/openssl/tests/tlsv1.2_wrapper_002.phpt b/ext/openssl/tests/tlsv1.2_wrapper_002.phpt
new file mode 100644
index 0000000000..cb3f4106c7
--- /dev/null
+++ b/ext/openssl/tests/tlsv1.2_wrapper_002.phpt
@@ -0,0 +1,46 @@
+--TEST--
+tlsv1.2 stream wrapper
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (OPENSSL_VERSION_NUMBER < 0x10001001) die("skip OpenSSL 1.0.1 required");
+if (!function_exists('pcntl_fork')) die("skip no fork");
+--FILE--
+<?php
+$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
+$ctx = stream_context_create(array('ssl' => array(
+ 'local_cert' => __DIR__ . '/streams_crypto_method.pem',
+)));
+
+$server = stream_socket_server('tlsv1.2://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
+var_dump($server);
+
+$pid = pcntl_fork();
+if ($pid == -1) {
+ die('could not fork');
+} elseif ($pid) {
+ $flags = STREAM_CLIENT_CONNECT;
+ $ctx = stream_context_create(array('ssl' => array(
+ 'verify_peer' => false
+ )));
+
+ $client = stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
+ var_dump($client);
+
+ $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
+ var_dump($client);
+
+ $client = @stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
+ var_dump($client);
+
+} else {
+ @pcntl_wait($status);
+ for ($i=0; $i < 3; $i++) {
+ @stream_socket_accept($server, 1);
+ }
+}
+--EXPECTF--
+resource(%d) of type (stream)
+resource(%d) of type (stream)
+bool(false)
+bool(false)
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index 1d1c91f132..9c9f2a3592 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -309,7 +309,7 @@ static inline int php_openssl_setup_crypto(php_stream *stream,
php_stream_xport_crypto_param *cparam
TSRMLS_DC)
{
- SSL_METHOD *method;
+ const SSL_METHOD *method;
long ssl_ctx_options = SSL_OP_ALL;
if (sslsock->ssl_handle) {
@@ -346,6 +346,24 @@ static inline int php_openssl_setup_crypto(php_stream *stream,
sslsock->is_client = 1;
method = TLSv1_client_method();
break;
+ case STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT:
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+ sslsock->is_client = 1;
+ method = TLSv1_1_client_method();
+ break;
+#else
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "TLSv1.1 support is not compiled into the OpenSSL library PHP is linked against");
+ return -1;
+#endif
+ case STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT:
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+ sslsock->is_client = 1;
+ method = TLSv1_2_client_method();
+ break;
+#else
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "TLSv1.2 support is not compiled into the OpenSSL library PHP is linked against");
+ return -1;
+#endif
case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
sslsock->is_client = 0;
method = SSLv23_server_method();
@@ -367,6 +385,24 @@ static inline int php_openssl_setup_crypto(php_stream *stream,
sslsock->is_client = 0;
method = TLSv1_server_method();
break;
+ case STREAM_CRYPTO_METHOD_TLSv1_1_SERVER:
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+ sslsock->is_client = 0;
+ method = TLSv1_1_server_method();
+ break;
+#else
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "TLSv1.1 support is not compiled into the OpenSSL library PHP is linked against");
+ return -1;
+#endif
+ case STREAM_CRYPTO_METHOD_TLSv1_2_SERVER:
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+ sslsock->is_client = 0;
+ method = TLSv1_2_server_method();
+ break;
+#else
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "TLSv1.2 support is not compiled into the OpenSSL library PHP is linked against");
+ return -1;
+#endif
default:
return -1;
@@ -667,6 +703,12 @@ static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_
case STREAM_CRYPTO_METHOD_TLS_CLIENT:
sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
break;
+ case STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT:
+ sock->method = STREAM_CRYPTO_METHOD_TLSv1_1_SERVER;
+ break;
+ case STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT:
+ sock->method = STREAM_CRYPTO_METHOD_TLSv1_2_SERVER;
+ break;
default:
break;
}
@@ -853,8 +895,32 @@ php_stream_ops php_openssl_socket_ops = {
php_openssl_sockop_set_option,
};
-static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) {
+static int get_crypto_method(php_stream_context *ctx) {
+ if (ctx) {
+ zval **val = NULL;
+ long crypto_method;
+
+ if (php_stream_context_get_option(ctx, "ssl", "crypto_method", &val) == SUCCESS) {
+ convert_to_long_ex(val);
+ crypto_method = (long)Z_LVAL_PP(val);
+
+ switch (crypto_method) {
+ case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
+ case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
+ case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
+ case STREAM_CRYPTO_METHOD_TLS_CLIENT:
+ case STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT:
+ case STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT:
+ return crypto_method;
+ }
+
+ }
+ }
+
+ return STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
+}
+static char * get_sni(php_stream_context *ctx, const char *resourcename, size_t resourcenamelen, int is_persistent TSRMLS_DC) {
php_url *url;
if (ctx) {
@@ -900,8 +966,8 @@ static char * get_sni(php_stream_context *ctx, char *resourcename, long resource
return NULL;
}
-php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
- char *resourcename, long resourcenamelen,
+php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
+ const char *resourcename, size_t resourcenamelen,
const char *persistent_id, int options, int flags,
struct timeval *timeout,
php_stream_context *context STREAMS_DC TSRMLS_DC)
@@ -939,7 +1005,12 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
if (strncmp(proto, "ssl", protolen) == 0) {
sslsock->enable_on_connect = 1;
- sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
+
+ /* General ssl:// transports can use a number
+ * of crypto methods. The actual methhod can be
+ * provided in the streams context options.
+ */
+ sslsock->method = get_crypto_method(context);
} else if (strncmp(proto, "sslv2", protolen) == 0) {
#ifdef OPENSSL_NO_SSL2
php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
@@ -954,8 +1025,24 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
} else if (strncmp(proto, "tls", protolen) == 0) {
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
+ } else if (strncmp(proto, "tlsv1.1", protolen) == 0) {
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+ sslsock->enable_on_connect = 1;
+ sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
+#else
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "TLSv1.1 support is not compiled into the OpenSSL library PHP is linked against");
+ return NULL;
+#endif
+ } else if (strncmp(proto, "tlsv1.2", protolen) == 0) {
+#if OPENSSL_VERSION_NUMBER >= 0x10001001L
+ sslsock->enable_on_connect = 1;
+ sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
+#else
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "TLSv1.2 support is not compiled into the OpenSSL library PHP is linked against");
+ return NULL;
+#endif
}
-
+
return stream;
}