diff options
Diffstat (limited to 'ext/openssl')
-rw-r--r-- | ext/openssl/openssl.c | 556 | ||||
-rw-r--r-- | ext/openssl/php_openssl.h | 6 | ||||
-rw-r--r-- | ext/openssl/tests/bug65729.pem | 28 | ||||
-rw-r--r-- | ext/openssl/tests/bug65729.phpt | 54 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_peer_fingerprint.phpt | 62 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_spki_export.phpt | 62 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_spki_export_challenge.phpt | 105 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_spki_new.phpt | 77 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_spki_verify.phpt | 105 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_x509_fingerprint.phpt | 47 | ||||
-rw-r--r-- | ext/openssl/tests/san-ca.pem | 15 | ||||
-rw-r--r-- | ext/openssl/tests/san-cert.pem | 31 | ||||
-rw-r--r-- | ext/openssl/tests/san_peer_matching.phpt | 60 | ||||
-rw-r--r-- | ext/openssl/tests/streams_crypto_method.pem | 33 | ||||
-rw-r--r-- | ext/openssl/tests/streams_crypto_method.phpt | 77 | ||||
-rw-r--r-- | ext/openssl/tests/tlsv1.1_wrapper_001.phpt | 46 | ||||
-rw-r--r-- | ext/openssl/tests/tlsv1.2_wrapper_002.phpt | 46 | ||||
-rw-r--r-- | ext/openssl/xp_ssl.c | 99 |
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 **)¤t, &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; } |