summaryrefslogtreecommitdiff
path: root/ext/openssl/openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/openssl/openssl.c')
-rw-r--r--ext/openssl/openssl.c1848
1 files changed, 1381 insertions, 467 deletions
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index e2f9fafacf..9198530b1d 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -17,6 +17,7 @@
| Sascha Kettler <kettler@gmx.net> |
| Pierre-Alain Joye <pierre@php.net> |
| Marc Delling <delling@silpion.de> (PKCS12 functions) |
+ | Jakub Zelenka <bukka@php.net> |
+----------------------------------------------------------------------+
*/
@@ -72,20 +73,22 @@
#ifdef HAVE_OPENSSL_MD2_H
#define OPENSSL_ALGO_MD2 4
#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define OPENSSL_ALGO_DSS1 5
-#if OPENSSL_VERSION_NUMBER >= 0x0090708fL
+#endif
#define OPENSSL_ALGO_SHA224 6
#define OPENSSL_ALGO_SHA256 7
#define OPENSSL_ALGO_SHA384 8
#define OPENSSL_ALGO_SHA512 9
#define OPENSSL_ALGO_RMD160 10
-#endif
#define DEBUG_SMIME 0
#if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC)
#define HAVE_EVP_PKEY_EC 1
#endif
+ZEND_DECLARE_MODULE_GLOBALS(openssl)
+
/* FIXME: Use the openssl constants instead of
* enum. It is now impossible to match real values
* against php constants. Also sorry to break the
@@ -116,6 +119,9 @@ enum php_openssl_cipher_type {
PHP_FUNCTION(openssl_get_md_methods);
PHP_FUNCTION(openssl_get_cipher_methods);
+#ifdef HAVE_EVP_PKEY_EC
+PHP_FUNCTION(openssl_get_curve_names);
+#endif
PHP_FUNCTION(openssl_digest);
PHP_FUNCTION(openssl_encrypt);
@@ -262,7 +268,6 @@ ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_details, 0)
ZEND_ARG_INFO(0, key)
ZEND_END_ARG_INFO()
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pbkdf2, 0, 0, 4)
ZEND_ARG_INFO(0, password)
ZEND_ARG_INFO(0, salt)
@@ -270,7 +275,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pbkdf2, 0, 0, 4)
ZEND_ARG_INFO(0, iterations)
ZEND_ARG_INFO(0, digest_algorithm)
ZEND_END_ARG_INFO()
-#endif
ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2)
ZEND_ARG_INFO(0, filename)
@@ -377,6 +381,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_cipher_methods, 0, 0, 0)
ZEND_ARG_INFO(0, aliases)
ZEND_END_ARG_INFO()
+#ifdef HAVE_EVP_PKEY_EC
+ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_curve_names, 0, 0, 0)
+ZEND_END_ARG_INFO()
+#endif
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_digest, 0, 0, 2)
ZEND_ARG_INFO(0, data)
ZEND_ARG_INFO(0, method)
@@ -389,6 +398,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_encrypt, 0, 0, 3)
ZEND_ARG_INFO(0, password)
ZEND_ARG_INFO(0, options)
ZEND_ARG_INFO(0, iv)
+ ZEND_ARG_INFO(1, tag)
+ ZEND_ARG_INFO(0, aad)
+ ZEND_ARG_INFO(0, tag_length)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3)
@@ -397,6 +409,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3)
ZEND_ARG_INFO(0, password)
ZEND_ARG_INFO(0, options)
ZEND_ARG_INFO(0, iv)
+ ZEND_ARG_INFO(0, tag)
+ ZEND_ARG_INFO(0, aad)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_openssl_cipher_iv_length, 0)
@@ -491,9 +505,7 @@ const zend_function_entry openssl_functions[] = {
PHP_FE(openssl_seal, arginfo_openssl_seal)
PHP_FE(openssl_open, arginfo_openssl_open)
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
PHP_FE(openssl_pbkdf2, arginfo_openssl_pbkdf2)
-#endif
/* for S/MIME handling */
PHP_FE(openssl_pkcs7_verify, arginfo_openssl_pkcs7_verify)
@@ -508,6 +520,9 @@ const zend_function_entry openssl_functions[] = {
PHP_FE(openssl_get_md_methods, arginfo_openssl_get_md_methods)
PHP_FE(openssl_get_cipher_methods, arginfo_openssl_get_cipher_methods)
+#ifdef HAVE_EVP_PKEY_EC
+ PHP_FE(openssl_get_curve_names, arginfo_openssl_get_curve_names)
+#endif
PHP_FE(openssl_dh_compute_key, arginfo_openssl_dh_compute_key)
@@ -529,7 +544,11 @@ zend_module_entry openssl_module_entry = {
NULL,
PHP_MINFO(openssl),
PHP_OPENSSL_VERSION,
- STANDARD_MODULE_PROPERTIES
+ PHP_MODULE_GLOBALS(openssl),
+ PHP_GINIT(openssl),
+ PHP_GSHUTDOWN(openssl),
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */
@@ -537,6 +556,131 @@ zend_module_entry openssl_module_entry = {
ZEND_GET_MODULE(openssl)
#endif
+/* {{{ OpenSSL compatibility functions and macros */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#define EVP_PKEY_get0_RSA(_pkey) _pkey->pkey.rsa
+#define EVP_PKEY_get0_DH(_pkey) _pkey->pkey.dh
+#define EVP_PKEY_get0_DSA(_pkey) _pkey->pkey.dsa
+#define EVP_PKEY_get0_EC_KEY(_pkey) _pkey->pkey.ec
+
+static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
+{
+ r->n = n;
+ r->e = e;
+ r->d = d;
+
+ return 1;
+}
+
+static int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
+{
+ r->p = p;
+ r->q = q;
+
+ return 1;
+}
+
+static int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
+{
+ r->dmp1 = dmp1;
+ r->dmq1 = dmq1;
+ r->iqmp = iqmp;
+
+ return 1;
+}
+
+static void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+{
+ *n = r->n;
+ *e = r->e;
+ *d = r->d;
+}
+
+static void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
+{
+ *p = r->p;
+ *q = r->q;
+}
+
+static void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp)
+{
+ *dmp1 = r->dmp1;
+ *dmq1 = r->dmq1;
+ *iqmp = r->iqmp;
+}
+
+static void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
+{
+ *p = dh->p;
+ *q = dh->q;
+ *g = dh->g;
+}
+
+static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
+{
+ dh->p = p;
+ dh->q = q;
+ dh->g = g;
+
+ return 1;
+}
+
+static void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
+{
+ *pub_key = dh->pub_key;
+ *priv_key = dh->priv_key;
+}
+
+static int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
+{
+ dh->pub_key = pub_key;
+ dh->priv_key = priv_key;
+
+ return 1;
+}
+
+static void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
+{
+ *p = d->p;
+ *q = d->q;
+ *g = d->g;
+}
+
+int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
+{
+ d->p = p;
+ d->q = q;
+ d->g = g;
+
+ return 1;
+}
+
+static void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
+{
+ *pub_key = d->pub_key;
+ *priv_key = d->priv_key;
+}
+
+int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
+{
+ d->pub_key = pub_key;
+ d->priv_key = priv_key;
+
+ return 1;
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+
+static int X509_get_signature_nid(const X509 *x)
+{
+ return OBJ_obj2nid(x->sig_alg->algorithm);
+}
+
+#endif
+
+#endif
+/* }}} */
+
/* number conversion flags checks */
#define PHP_OPENSSL_CHECK_NUMBER_CONVERSION(_cond, _name) \
do { \
@@ -555,6 +699,32 @@ ZEND_GET_MODULE(openssl)
#define PHP_OPENSSL_CHECK_LONG_TO_INT(_var, _name) \
PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_LONG_EXCEEDS_INT(_var), _name)
+/* {{{ php_openssl_store_errors */
+void php_openssl_store_errors()
+{
+ struct php_openssl_errors *errors;
+ int error_code = ERR_get_error();
+
+ if (!error_code) {
+ return;
+ }
+
+ if (!OPENSSL_G(errors)) {
+ OPENSSL_G(errors) = pecalloc(1, sizeof(struct php_openssl_errors), 1);
+ }
+
+ errors = OPENSSL_G(errors);
+
+ do {
+ errors->top = (errors->top + 1) % ERR_NUM_ERRORS;
+ if (errors->top == errors->bottom) {
+ errors->bottom = (errors->bottom + 1) % ERR_NUM_ERRORS;
+ }
+ errors->buffer[errors->top] = error_code;
+ } while ((error_code = ERR_get_error()));
+
+}
+/* }}} */
static int le_key;
static int le_x509;
@@ -616,13 +786,8 @@ int php_openssl_get_ssl_stream_data_index()
static char default_ssl_conf_filename[MAXPATHLEN];
struct php_x509_request { /* {{{ */
-#if OPENSSL_VERSION_NUMBER >= 0x10000002L
LHASH_OF(CONF_VALUE) * global_config; /* Global SSL config */
LHASH_OF(CONF_VALUE) * req_config; /* SSL config for this request */
-#else
- LHASH * global_config; /* Global SSL config */
- LHASH * req_config; /* SSL config for this request */
-#endif
const EVP_MD * md_alg;
const EVP_MD * digest;
char * section_name,
@@ -635,6 +800,10 @@ struct php_x509_request { /* {{{ */
int priv_key_encrypt;
+#ifdef HAVE_EVP_PKEY_EC
+ int curve_name;
+#endif
+
EVP_PKEY * priv_key;
const EVP_CIPHER * priv_key_encrypt_cipher;
@@ -702,6 +871,8 @@ static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int s
} else {
add_assoc_stringl(&subitem, sname, (char *)to_add, to_add_len);
}
+ } else {
+ php_openssl_store_errors();
}
}
if (key != NULL) {
@@ -722,7 +893,7 @@ static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr) /* {{{ */
This is how the time string is formatted:
snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
- ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
+ ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
*/
time_t ret;
@@ -736,7 +907,7 @@ static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr) /* {{{ */
return (time_t)-1;
}
- if (ASN1_STRING_length(timestr) != strlen((const char*)ASN1_STRING_data(timestr))) {
+ if ((size_t)ASN1_STRING_length(timestr) != strlen((const char*)ASN1_STRING_data(timestr))) {
php_error_docref(NULL, E_WARNING, "illegal length in timestamp");
return (time_t)-1;
}
@@ -808,17 +979,14 @@ static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr) /* {{{ */
}
/* }}} */
-#if OPENSSL_VERSION_NUMBER >= 0x10000002L
static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH_OF(CONF_VALUE) * config) /* {{{ */
-#else
-static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH * config)
-#endif
{
X509V3_CTX ctx;
X509V3_set_ctx_test(&ctx);
X509V3_set_conf_lhash(&ctx, config);
if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Error loading %s section %s of %s",
section_label,
section,
@@ -838,16 +1006,20 @@ static int add_oid_section(struct php_x509_request * req) /* {{{ */
str = CONF_get_string(req->req_config, NULL, "oid_section");
if (str == NULL) {
+ php_openssl_store_errors();
return SUCCESS;
}
sktmp = CONF_get_section(req->req_config, str);
if (sktmp == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "problem loading oid section %s", str);
return FAILURE;
}
for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
cnf = sk_CONF_VALUE_value(sktmp, i);
- if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) {
+ if (OBJ_sn2nid(cnf->name) == NID_undef && OBJ_ln2nid(cnf->name) == NID_undef &&
+ OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "problem creating object %s=%s", cnf->name, cnf->value);
return FAILURE;
}
@@ -864,10 +1036,16 @@ static int add_oid_section(struct php_x509_request * req) /* {{{ */
req->config_filename, req->var, req->req_config) == FAILURE) return FAILURE
#define SET_OPTIONAL_STRING_ARG(key, varname, defval) \
- if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_STRING) \
- varname = Z_STRVAL_P(item); \
- else \
- varname = defval
+ do { \
+ if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_STRING) { \
+ varname = Z_STRVAL_P(item); \
+ } else { \
+ varname = defval; \
+ if (varname == NULL) { \
+ php_openssl_store_errors(); \
+ } \
+ } \
+ } while(0)
#define SET_OPTIONAL_LONG_ARG(key, varname, defval) \
if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_LONG) \
@@ -904,19 +1082,25 @@ static int php_openssl_parse_config(struct php_x509_request * req, zval * option
SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename);
SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req");
req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL);
+ if (req->global_config == NULL) {
+ php_openssl_store_errors();
+ }
req->req_config = CONF_load(NULL, req->config_filename, NULL);
-
if (req->req_config == NULL) {
+ php_openssl_store_errors();
return FAILURE;
}
/* read in the oids */
str = CONF_get_string(req->req_config, NULL, "oid_file");
- if (str && !php_openssl_open_base_dir_chk(str)) {
+ if (str == NULL) {
+ php_openssl_store_errors();
+ } else if (!php_openssl_open_base_dir_chk(str)) {
BIO *oid_bio = BIO_new_file(str, "r");
if (oid_bio) {
OBJ_create_objects(oid_bio);
BIO_free(oid_bio);
+ php_openssl_store_errors();
}
}
if (add_oid_section(req) == FAILURE) {
@@ -939,8 +1123,10 @@ static int php_openssl_parse_config(struct php_x509_request * req, zval * option
str = CONF_get_string(req->req_config, req->section_name, "encrypt_rsa_key");
if (str == NULL) {
str = CONF_get_string(req->req_config, req->section_name, "encrypt_key");
+ /* it is sure that there are some errrors as str was NULL for encrypt_rsa_key */
+ php_openssl_store_errors();
}
- if (str && strcmp(str, "no") == 0) {
+ if (str != NULL && strcmp(str, "no") == 0) {
req->priv_key_encrypt = 0;
} else {
req->priv_key_encrypt = 1;
@@ -967,18 +1153,35 @@ static int php_openssl_parse_config(struct php_x509_request * req, zval * option
if (req->digest_name == NULL) {
req->digest_name = CONF_get_string(req->req_config, req->section_name, "default_md");
}
- if (req->digest_name) {
+ if (req->digest_name != NULL) {
req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name);
+ } else {
+ php_openssl_store_errors();
}
if (req->md_alg == NULL) {
req->md_alg = req->digest = EVP_sha1();
+ php_openssl_store_errors();
}
PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section);
+#ifdef HAVE_EVP_PKEY_EC
+ /* set the ec group curve name */
+ req->curve_name = NID_undef;
+ if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), "curve_name", sizeof("curve_name")-1)) != NULL
+ && Z_TYPE_P(item) == IS_STRING) {
+ req->curve_name = OBJ_sn2nid(Z_STRVAL_P(item));
+ if (req->curve_name == NID_undef) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown elliptic curve (short) name %s", Z_STRVAL_P(item));
+ return FAILURE;
+ }
+ }
+#endif
/* set the string mask */
str = CONF_get_string(req->req_config, req->section_name, "string_mask");
- if (str && !ASN1_STRING_set_default_mask_asc(str)) {
+ if (str == NULL) {
+ php_openssl_store_errors();
+ } else if (!ASN1_STRING_set_default_mask_asc(str)) {
php_error_docref(NULL, E_WARNING, "Invalid global string mask setting %s", str);
return FAILURE;
}
@@ -1041,6 +1244,7 @@ static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *se
}
if (file == NULL || !RAND_load_file(file, -1)) {
if (RAND_status() == 0) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "unable to load random state; not enough random data!");
return FAILURE;
}
@@ -1066,6 +1270,7 @@ static int php_openssl_write_rand_file(const char * file, int egdsocket, int see
}
PHP_OPENSSL_RAND_ADD_TIME();
if (file == NULL || !RAND_write_file(file)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "unable to write random state");
return FAILURE;
}
@@ -1091,10 +1296,11 @@ static EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { /* {{{ */
mdtype = (EVP_MD *) EVP_md2();
break;
#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
case OPENSSL_ALGO_DSS1:
mdtype = (EVP_MD *) EVP_dss1();
break;
-#if OPENSSL_VERSION_NUMBER >= 0x0090708fL
+#endif
case OPENSSL_ALGO_SHA224:
mdtype = (EVP_MD *) EVP_sha224();
break;
@@ -1110,7 +1316,6 @@ static EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { /* {{{ */
case OPENSSL_ALGO_RMD160:
mdtype = (EVP_MD *) EVP_ripemd160();
break;
-#endif
default:
return NULL;
break;
@@ -1184,6 +1389,12 @@ PHP_MINIT_FUNCTION(openssl)
OpenSSL_add_all_digests();
OpenSSL_add_all_algorithms();
+#if !defined(OPENSSL_NO_AES) && defined(EVP_CIPH_CCM_MODE) && OPENSSL_VERSION_NUMBER < 0x100020000
+ EVP_add_cipher(EVP_aes_128_ccm());
+ EVP_add_cipher(EVP_aes_192_ccm());
+ EVP_add_cipher(EVP_aes_256_ccm());
+#endif
+
SSL_load_error_strings();
/* register a resource id number with OpenSSL so that we can map SSL -> stream structures in
@@ -1211,14 +1422,14 @@ PHP_MINIT_FUNCTION(openssl)
#ifdef HAVE_OPENSSL_MD2_H
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD2", OPENSSL_ALGO_MD2, CONST_CS|CONST_PERSISTENT);
#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT);
-#if OPENSSL_VERSION_NUMBER >= 0x0090708fL
+#endif
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA224", OPENSSL_ALGO_SHA224, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA256", OPENSSL_ALGO_SHA256, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA384", OPENSSL_ALGO_SHA384, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA512", OPENSSL_ALGO_SHA512, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_RMD160", OPENSSL_ALGO_RMD160, CONST_CS|CONST_PERSISTENT);
-#endif
/* flags for S/MIME */
REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT);
@@ -1268,8 +1479,8 @@ PHP_MINIT_FUNCTION(openssl)
REGISTER_LONG_CONSTANT("OPENSSL_RAW_DATA", OPENSSL_RAW_DATA, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ZERO_PADDING", OPENSSL_ZERO_PADDING, CONST_CS|CONST_PERSISTENT);
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
- /* SNI support included in OpenSSL >= 0.9.8j */
+#ifndef OPENSSL_NO_TLSEXT
+ /* SNI support included */
REGISTER_LONG_CONSTANT("OPENSSL_TLSEXT_SERVER_NAME", 1, CONST_CS|CONST_PERSISTENT);
#endif
@@ -1292,15 +1503,10 @@ PHP_MINIT_FUNCTION(openssl)
#ifndef OPENSSL_NO_SSL3
php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory);
#endif
-#ifndef OPENSSL_NO_SSL2
- php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory);
-#endif
php_stream_xport_register("tls", php_openssl_ssl_socket_factory);
php_stream_xport_register("tlsv1.0", php_openssl_ssl_socket_factory);
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
php_stream_xport_register("tlsv1.1", php_openssl_ssl_socket_factory);
php_stream_xport_register("tlsv1.2", php_openssl_ssl_socket_factory);
-#endif
/* override the default tcp socket provider */
php_stream_xport_register("tcp", php_openssl_ssl_socket_factory);
@@ -1314,6 +1520,27 @@ PHP_MINIT_FUNCTION(openssl)
}
/* }}} */
+/* {{{ PHP_GINIT_FUNCTION
+*/
+PHP_GINIT_FUNCTION(openssl)
+{
+#if defined(COMPILE_DL_OPENSSL) && defined(ZTS)
+ ZEND_TSRMLS_CACHE_UPDATE();
+#endif
+ openssl_globals->errors = NULL;
+}
+/* }}} */
+
+/* {{{ PHP_GSHUTDOWN_FUNCTION
+*/
+PHP_GSHUTDOWN_FUNCTION(openssl)
+{
+ if (openssl_globals->errors) {
+ pefree(openssl_globals->errors, 1);
+ }
+}
+/* }}} */
+
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(openssl)
@@ -1334,29 +1561,22 @@ PHP_MSHUTDOWN_FUNCTION(openssl)
{
EVP_cleanup();
-#if OPENSSL_VERSION_NUMBER >= 0x00090805f
/* prevent accessing locking callback from unloaded extension */
CRYPTO_set_locking_callback(NULL);
/* free allocated error strings */
ERR_free_strings();
-#endif
php_unregister_url_stream_wrapper("https");
php_unregister_url_stream_wrapper("ftps");
php_stream_xport_unregister("ssl");
-#ifndef OPENSSL_NO_SSL2
- php_stream_xport_unregister("sslv2");
-#endif
#ifndef OPENSSL_NO_SSL3
php_stream_xport_unregister("sslv3");
#endif
php_stream_xport_unregister("tls");
php_stream_xport_unregister("tlsv1.0");
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
php_stream_xport_unregister("tlsv1.1");
php_stream_xport_unregister("tlsv1.2");
-#endif
/* reinstate the default tcp handler */
php_stream_xport_register("tcp", php_stream_generic_socket_factory);
@@ -1401,6 +1621,7 @@ PHP_FUNCTION(openssl_get_cert_locations)
static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_resource **resourceval)
{
X509 *cert = NULL;
+ BIO *in;
if (resourceval) {
*resourceval = NULL;
@@ -1430,8 +1651,6 @@ static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_reso
convert_to_string_ex(val);
if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {
- /* read cert from the named file */
- BIO *in;
if (php_openssl_open_base_dir_chk(Z_STRVAL_P(val) + (sizeof("file://") - 1))) {
return NULL;
@@ -1439,15 +1658,16 @@ static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_reso
in = BIO_new_file(Z_STRVAL_P(val) + (sizeof("file://") - 1), "r");
if (in == NULL) {
+ php_openssl_store_errors();
return NULL;
}
cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
- BIO_free(in);
+
} else {
- BIO *in;
in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
if (in == NULL) {
+ php_openssl_store_errors();
return NULL;
}
#ifdef TYPEDEF_D2I_OF
@@ -1455,10 +1675,18 @@ static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_reso
#else
cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
#endif
- BIO_free(in);
}
- if (cert && makeresource && resourceval) {
+ if (!BIO_free(in)) {
+ php_openssl_store_errors();
+ }
+
+ if (cert == NULL) {
+ php_openssl_store_errors();
+ return NULL;
+ }
+
+ if (makeresource && resourceval) {
*resourceval = zend_register_resource(cert, le_x509);
}
return cert;
@@ -1495,19 +1723,25 @@ PHP_FUNCTION(openssl_x509_export_to_file)
bio_out = BIO_new_file(filename, "w");
if (bio_out) {
- if (!notext) {
- X509_print(bio_out, cert);
+ if (!notext && !X509_print(bio_out, cert)) {
+ php_openssl_store_errors();
+ }
+ if (!PEM_write_bio_X509(bio_out, cert)) {
+ php_openssl_store_errors();
}
- PEM_write_bio_X509(bio_out, cert);
RETVAL_TRUE;
} else {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
}
if (certresource == NULL && cert) {
X509_free(cert);
}
- BIO_free(bio_out);
+
+ if (!BIO_free(bio_out)) {
+ php_openssl_store_errors();
+ }
}
/* }}} */
@@ -1557,29 +1791,34 @@ PHP_FUNCTION(openssl_spki_new)
}
if ((spki = NETSCAPE_SPKI_new()) == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to create new SPKAC");
goto cleanup;
}
if (challenge) {
if (!ASN1_STRING_set(spki->spkac->challenge, challenge, (int)challenge_len)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to set challenge data");
goto cleanup;
}
}
if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to embed public key");
goto cleanup;
}
if (!NETSCAPE_SPKI_sign(spki, pkey, mdtype)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to sign with specified algorithm");
goto cleanup;
}
spkstr = NETSCAPE_SPKI_b64_encode(spki);
if (!spkstr){
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to encode SPKAC");
goto cleanup;
}
@@ -1644,12 +1883,14 @@ PHP_FUNCTION(openssl_spki_verify)
spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
if (spki == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
goto cleanup;
}
pkey = X509_PUBKEY_get(spki->spkac->pubkey);
if (pkey == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
goto cleanup;
}
@@ -1670,6 +1911,8 @@ cleanup:
if (i > 0) {
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
}
/* }}} */
@@ -1706,12 +1949,14 @@ PHP_FUNCTION(openssl_spki_export)
spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
if (spki == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
goto cleanup;
}
pkey = X509_PUBKEY_get(spki->spkac->pubkey);
if (pkey == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
goto cleanup;
}
@@ -1722,6 +1967,8 @@ PHP_FUNCTION(openssl_spki_export)
BIO_get_mem_ptr(out, &bio_buf);
RETVAL_STRINGL((char *)bio_buf->data, bio_buf->length);
+ } else {
+ php_openssl_store_errors();
}
goto cleanup;
@@ -1775,6 +2022,7 @@ PHP_FUNCTION(openssl_spki_export_challenge)
spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
if (spki == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to decode SPKAC");
goto cleanup;
}
@@ -1811,8 +2059,12 @@ PHP_FUNCTION(openssl_x509_export)
}
bio_out = BIO_new(BIO_s_mem());
- if (!notext) {
- X509_print(bio_out, cert);
+ if (!bio_out) {
+ php_openssl_store_errors();
+ goto cleanup;
+ }
+ if (!notext && !X509_print(bio_out, cert)) {
+ php_openssl_store_errors();
}
if (PEM_write_bio_X509(bio_out, cert)) {
BUF_MEM *bio_buf;
@@ -1822,12 +2074,16 @@ PHP_FUNCTION(openssl_x509_export)
ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
- if (certresource == NULL && cert) {
+ BIO_free(bio_out);
+
+cleanup:
+ if (certresource == NULL && cert != NULL) {
X509_free(cert);
}
- BIO_free(bio_out);
}
/* }}} */
@@ -1842,6 +2098,7 @@ zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, zend_b
php_error_docref(NULL, E_WARNING, "Unknown signature algorithm");
return NULL;
} else if (!X509_digest(peer, mdtype, md, &n)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_ERROR, "Could not generate signature");
return NULL;
}
@@ -1929,6 +2186,7 @@ static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
{
GENERAL_NAMES *names;
const X509V3_EXT_METHOD *method = NULL;
+ ASN1_OCTET_STRING *extension_data;
long i, length, num;
const unsigned char *p;
@@ -1937,8 +2195,9 @@ static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
return -1;
}
- p = extension->value->data;
- length = extension->value->length;
+ extension_data = X509_EXTENSION_get_data(extension);
+ p = extension_data->data;
+ length = extension_data->length;
if (method->it) {
names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, &p, length,
ASN1_ITEM_ptr(method->it)));
@@ -1946,6 +2205,7 @@ static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
names = (GENERAL_NAMES*) (method->d2i(NULL, &p, length));
}
if (names == NULL) {
+ php_openssl_store_errors();
return -1;
}
@@ -2001,6 +2261,8 @@ PHP_FUNCTION(openssl_x509_parse)
char * tmpstr;
zval subitem;
X509_EXTENSION *extension;
+ X509_NAME *subject_name;
+ char *cert_name;
char *extname;
BIO *bio_out;
BUF_MEM *bio_buf;
@@ -2015,12 +2277,12 @@ PHP_FUNCTION(openssl_x509_parse)
}
array_init(return_value);
- if (cert->name) {
- add_assoc_string(return_value, "name", cert->name);
- }
-/* add_assoc_bool(return_value, "valid", cert->valid); */
+ subject_name = X509_get_subject_name(cert);
+ cert_name = X509_NAME_oneline(subject_name, NULL, 0);
+ add_assoc_string(return_value, "name", cert_name);
+ OPENSSL_free(cert_name);
- add_assoc_name_entry(return_value, "subject", X509_get_subject_name(cert), useshortnames);
+ add_assoc_name_entry(return_value, "subject", subject_name, useshortnames);
/* hash as used in CA directories to lookup cert by subject name */
{
char buf[32];
@@ -2044,7 +2306,7 @@ PHP_FUNCTION(openssl_x509_parse)
add_assoc_string(return_value, "alias", tmpstr);
}
- sig_nid = OBJ_obj2nid((cert)->sig_alg->algorithm);
+ sig_nid = X509_get_signature_nid(cert);
add_assoc_string(return_value, "signatureTypeSN", (char*)OBJ_nid2sn(sig_nid));
add_assoc_string(return_value, "signatureTypeLN", (char*)OBJ_nid2ln(sig_nid));
add_assoc_long(return_value, "signatureTypeNID", sig_nid);
@@ -2092,6 +2354,10 @@ PHP_FUNCTION(openssl_x509_parse)
extname = buf;
}
bio_out = BIO_new(BIO_s_mem());
+ if (bio_out == NULL) {
+ php_openssl_store_errors();
+ RETURN_FALSE;
+ }
if (nid == NID_subject_alt_name) {
if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
BIO_get_mem_ptr(bio_out, &bio_buf);
@@ -2130,6 +2396,7 @@ static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
X509_INFO *xi;
if(!(stack = sk_X509_new_null())) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_ERROR, "memory allocation failure");
goto end;
}
@@ -2140,6 +2407,7 @@ static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
}
if(!(in=BIO_new_file(certfile, "r"))) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening the file, %s", certfile);
sk_X509_free(stack);
goto end;
@@ -2147,6 +2415,7 @@ static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
/* This loads from a file, a stack of x509/crl/pkey sets */
if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error reading the file, %s", certfile);
sk_X509_free(stack);
goto end;
@@ -2183,14 +2452,22 @@ static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain,
csc = X509_STORE_CTX_new();
if (csc == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_ERROR, "memory allocation failure");
return 0;
}
- X509_STORE_CTX_init(csc, ctx, x, untrustedchain);
- if(purpose >= 0) {
- X509_STORE_CTX_set_purpose(csc, purpose);
+ if (!X509_STORE_CTX_init(csc, ctx, x, untrustedchain)) {
+ php_openssl_store_errors();
+ php_error_docref(NULL, E_WARNING, "cert store initialization failed");
+ return 0;
+ }
+ if (purpose >= 0 && !X509_STORE_CTX_set_purpose(csc, purpose)) {
+ php_openssl_store_errors();
}
ret = X509_verify_cert(csc);
+ if (ret < 0) {
+ php_openssl_store_errors();
+ }
X509_STORE_CTX_free(csc);
return ret;
@@ -2268,6 +2545,7 @@ static X509_STORE * setup_verify(zval * calist)
store = X509_STORE_new();
if (store == NULL) {
+ php_openssl_store_errors();
return NULL;
}
@@ -2283,6 +2561,7 @@ static X509_STORE * setup_verify(zval * calist)
if ((sb.st_mode & S_IFREG) == S_IFREG) {
file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_P(item), X509_FILETYPE_PEM)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error loading file %s", Z_STRVAL_P(item));
} else {
nfiles++;
@@ -2291,6 +2570,7 @@ static X509_STORE * setup_verify(zval * calist)
} else {
dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_P(item), X509_FILETYPE_PEM)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error loading directory %s", Z_STRVAL_P(item));
} else {
ndirs++;
@@ -2301,14 +2581,14 @@ static X509_STORE * setup_verify(zval * calist)
}
if (nfiles == 0) {
file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
- if (file_lookup) {
- X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT);
+ if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT)) {
+ php_openssl_store_errors();
}
}
if (ndirs == 0) {
dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
- if (dir_lookup) {
- X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT);
+ if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT)) {
+ php_openssl_store_errors();
}
}
return store;
@@ -2388,6 +2668,7 @@ static STACK_OF(X509) * php_array_to_X509_sk(zval * zcerts) /* {{{ */
cert = X509_dup(cert);
if (cert == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
@@ -2405,6 +2686,7 @@ static STACK_OF(X509) * php_array_to_X509_sk(zval * zcerts) /* {{{ */
if (certresource != NULL) {
cert = X509_dup(cert);
if (cert == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
}
@@ -2450,6 +2732,7 @@ PHP_FUNCTION(openssl_pkcs12_export_to_file)
goto cleanup;
}
if (cert && !X509_check_private_key(cert, priv_key)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "private key does not correspond to cert");
goto cleanup;
}
@@ -2473,19 +2756,24 @@ PHP_FUNCTION(openssl_pkcs12_export_to_file)
int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/
p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
+ if (p12 != NULL) {
+ bio_out = BIO_new_file(filename, "w");
+ if (bio_out != NULL) {
- bio_out = BIO_new_file(filename, "w");
- if (bio_out) {
+ i2d_PKCS12_bio(bio_out, p12);
+ BIO_free(bio_out);
- i2d_PKCS12_bio(bio_out, p12);
+ RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
+ php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
+ }
- RETVAL_TRUE;
+ PKCS12_free(p12);
} else {
- php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
+ php_openssl_store_errors();
}
- BIO_free(bio_out);
- PKCS12_free(p12);
php_sk_X509_free(ca);
cleanup:
@@ -2545,19 +2833,25 @@ PHP_FUNCTION(openssl_pkcs12_export)
p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
- bio_out = BIO_new(BIO_s_mem());
- if (i2d_PKCS12_bio(bio_out, p12)) {
- BUF_MEM *bio_buf;
+ if (p12 != NULL) {
+ bio_out = BIO_new(BIO_s_mem());
+ if (i2d_PKCS12_bio(bio_out, p12)) {
+ BUF_MEM *bio_buf;
- zval_dtor(zout);
- BIO_get_mem_ptr(bio_out, &bio_buf);
- ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
+ zval_dtor(zout);
+ BIO_get_mem_ptr(bio_out, &bio_buf);
+ ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
- RETVAL_TRUE;
- }
+ RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
+ }
- BIO_free(bio_out);
- PKCS12_free(p12);
+ BIO_free(bio_out);
+ PKCS12_free(p12);
+ } else {
+ php_openssl_store_errors();
+ }
php_sk_X509_free(ca);
cleanup:
@@ -2594,72 +2888,70 @@ PHP_FUNCTION(openssl_pkcs12_read)
bio_in = BIO_new(BIO_s_mem());
- if(0 >= BIO_write(bio_in, zp12, (int)zp12_len))
+ if (0 >= BIO_write(bio_in, zp12, (int)zp12_len)) {
+ php_openssl_store_errors();
goto cleanup;
+ }
- if(d2i_PKCS12_bio(bio_in, &p12)) {
- if(PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
- BIO * bio_out;
+ if (d2i_PKCS12_bio(bio_in, &p12) && PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
+ BIO * bio_out;
- zval_dtor(zout);
- array_init(zout);
+ zval_dtor(zout);
+ array_init(zout);
- bio_out = BIO_new(BIO_s_mem());
- if (PEM_write_bio_X509(bio_out, cert)) {
- BUF_MEM *bio_buf;
- BIO_get_mem_ptr(bio_out, &bio_buf);
- ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
- add_assoc_zval(zout, "cert", &zcert);
- }
- BIO_free(bio_out);
+ bio_out = BIO_new(BIO_s_mem());
+ if (PEM_write_bio_X509(bio_out, cert)) {
+ BUF_MEM *bio_buf;
+ BIO_get_mem_ptr(bio_out, &bio_buf);
+ ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
+ add_assoc_zval(zout, "cert", &zcert);
+ } else {
+ php_openssl_store_errors();
+ }
+ BIO_free(bio_out);
+
+ bio_out = BIO_new(BIO_s_mem());
+ if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
+ BUF_MEM *bio_buf;
+ BIO_get_mem_ptr(bio_out, &bio_buf);
+ ZVAL_STRINGL(&zpkey, bio_buf->data, bio_buf->length);
+ add_assoc_zval(zout, "pkey", &zpkey);
+ } else {
+ php_openssl_store_errors();
+ }
+ BIO_free(bio_out);
+
+ array_init(&zextracerts);
+
+ for (i=0;;i++) {
+ zval zextracert;
+ X509* aCA = sk_X509_pop(ca);
+ if (!aCA) break;
bio_out = BIO_new(BIO_s_mem());
- if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
+ if (PEM_write_bio_X509(bio_out, aCA)) {
BUF_MEM *bio_buf;
BIO_get_mem_ptr(bio_out, &bio_buf);
- ZVAL_STRINGL(&zpkey, bio_buf->data, bio_buf->length);
- add_assoc_zval(zout, "pkey", &zpkey);
+ ZVAL_STRINGL(&zextracert, bio_buf->data, bio_buf->length);
+ add_index_zval(&zextracerts, i, &zextracert);
+
}
BIO_free(bio_out);
- array_init(&zextracerts);
-
- for (i=0;;i++) {
- zval zextracert;
- X509* aCA = sk_X509_pop(ca);
- if (!aCA) break;
-
- /* fix for bug 69882 */
- {
- int err = ERR_peek_error();
- if (err == OPENSSL_ERROR_X509_PRIVATE_KEY_VALUES_MISMATCH) {
- ERR_get_error();
- }
- }
-
- bio_out = BIO_new(BIO_s_mem());
- if (PEM_write_bio_X509(bio_out, aCA)) {
- BUF_MEM *bio_buf;
- BIO_get_mem_ptr(bio_out, &bio_buf);
- ZVAL_STRINGL(&zextracert, bio_buf->data, bio_buf->length);
- add_index_zval(&zextracerts, i, &zextracert);
-
- }
- BIO_free(bio_out);
-
- X509_free(aCA);
- }
- if(ca) {
- sk_X509_free(ca);
- add_assoc_zval(zout, "extracerts", &zextracerts);
- } else {
- zval_dtor(&zextracerts);
- }
+ X509_free(aCA);
+ }
+ if(ca) {
+ sk_X509_free(ca);
+ add_assoc_zval(zout, "extracerts", &zextracerts);
+ } else {
+ zval_dtor(&zextracerts);
+ }
- RETVAL_TRUE;
+ RETVAL_TRUE;
- PKCS12_free(p12);
- }
+ PKCS12_free(p12);
+ } else {
+ php_openssl_store_errors();
}
cleanup:
@@ -2685,18 +2977,22 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name");
if (dn_sect == NULL) {
+ php_openssl_store_errors();
return FAILURE;
}
dn_sk = CONF_get_section(req->req_config, dn_sect);
if (dn_sk == NULL) {
+ php_openssl_store_errors();
return FAILURE;
}
attr_sect = CONF_get_string(req->req_config, req->section_name, "attributes");
if (attr_sect == NULL) {
+ php_openssl_store_errors();
attr_sk = NULL;
} else {
attr_sk = CONF_get_section(req->req_config, attr_sect);
if (attr_sk == NULL) {
+ php_openssl_store_errors();
return FAILURE;
}
}
@@ -2722,6 +3018,7 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8,
(unsigned char*)Z_STRVAL_P(item), -1, -1, 0))
{
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING,
"dn: add_entry_by_NID %d -> %s (failed; check error"
" queue and value of string_mask OpenSSL option "
@@ -2737,13 +3034,13 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
/* Finally apply defaults from config file */
for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
- int len;
+ size_t len;
char buffer[200 + 1]; /*200 + \0 !*/
v = sk_CONF_VALUE_value(dn_sk, i);
type = v->name;
- len = (int)strlen(type);
+ len = strlen(type);
if (len < sizeof("_default")) {
continue;
}
@@ -2775,6 +3072,7 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
continue;
}
if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
return FAILURE;
}
@@ -2797,6 +3095,7 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
nid = OBJ_txt2nid(ZSTR_VAL(strindex));
if (nid != NID_undef) {
if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)Z_STRVAL_P(item), -1, -1, 0)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_P(item));
return FAILURE;
}
@@ -2812,6 +3111,7 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
continue;
}
if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING,
"add1_attr_by_txt %s -> %s (failed; check error queue "
"and value of string_mask OpenSSL option if illegal "
@@ -2821,9 +3121,13 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
}
}
}
+ } else {
+ php_openssl_store_errors();
}
- X509_REQ_set_pubkey(csr, req->priv_key);
+ if (!X509_REQ_set_pubkey(csr, req->priv_key)) {
+ php_openssl_store_errors();
+ }
return SUCCESS;
}
/* }}} */
@@ -2866,7 +3170,17 @@ static X509_REQ * php_openssl_csr_from_zval(zval * val, int makeresource, zend_r
} else {
in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
}
+
+ if (in == NULL) {
+ php_openssl_store_errors();
+ return NULL;
+ }
+
csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
+ if (csr == NULL) {
+ php_openssl_store_errors();
+ }
+
BIO_free(in);
return csr;
@@ -2901,20 +3215,25 @@ PHP_FUNCTION(openssl_csr_export_to_file)
}
bio_out = BIO_new_file(filename, "w");
- if (bio_out) {
- if (!notext) {
- X509_REQ_print(bio_out, csr);
+ if (bio_out != NULL) {
+ if (!notext && !X509_REQ_print(bio_out, csr)) {
+ php_openssl_store_errors();
}
- PEM_write_bio_X509_REQ(bio_out, csr);
- RETVAL_TRUE;
+ if (!PEM_write_bio_X509_REQ(bio_out, csr)) {
+ php_error_docref(NULL, E_WARNING, "error writing PEM to file %s", filename);
+ php_openssl_store_errors();
+ } else {
+ RETVAL_TRUE;
+ }
+ BIO_free(bio_out);
} else {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
}
- if (csr_resource == NULL && csr) {
+ if (csr_resource == NULL && csr != NULL) {
X509_REQ_free(csr);
}
- BIO_free(bio_out);
}
/* }}} */
@@ -2943,8 +3262,8 @@ PHP_FUNCTION(openssl_csr_export)
/* export to a var */
bio_out = BIO_new(BIO_s_mem());
- if (!notext) {
- X509_REQ_print(bio_out, csr);
+ if (!notext && !X509_REQ_print(bio_out, csr)) {
+ php_openssl_store_errors();
}
if (PEM_write_bio_X509_REQ(bio_out, csr)) {
@@ -2955,6 +3274,8 @@ PHP_FUNCTION(openssl_csr_export)
ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (csr_resource == NULL && csr) {
@@ -3002,6 +3323,7 @@ PHP_FUNCTION(openssl_csr_sign)
goto cleanup;
}
if (cert && !X509_check_private_key(cert, priv_key)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "private key does not correspond to signing cert");
goto cleanup;
}
@@ -3012,12 +3334,14 @@ PHP_FUNCTION(openssl_csr_sign)
/* Check that the request matches the signature */
key = X509_REQ_get_pubkey(csr);
if (key == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error unpacking public key");
goto cleanup;
}
i = X509_REQ_verify(csr, key);
if (i < 0) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Signature verification problems");
goto cleanup;
}
@@ -3030,12 +3354,14 @@ PHP_FUNCTION(openssl_csr_sign)
new_cert = X509_new();
if (new_cert == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "No memory");
goto cleanup;
}
/* Version 3 cert */
- if (!X509_set_version(new_cert, 2))
+ if (!X509_set_version(new_cert, 2)) {
goto cleanup;
+ }
ASN1_INTEGER_set(X509_get_serialNumber(new_cert), (long)serial);
@@ -3046,12 +3372,14 @@ PHP_FUNCTION(openssl_csr_sign)
cert = new_cert;
}
if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
+ php_openssl_store_errors();
goto cleanup;
}
X509_gmtime_adj(X509_get_notBefore(new_cert), 0);
X509_gmtime_adj(X509_get_notAfter(new_cert), 60*60*24*(long)num_days);
i = X509_set_pubkey(new_cert, key);
if (!i) {
+ php_openssl_store_errors();
goto cleanup;
}
if (req.extensions_section) {
@@ -3060,12 +3388,14 @@ PHP_FUNCTION(openssl_csr_sign)
X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
X509V3_set_conf_lhash(&ctx, req.req_config);
if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) {
+ php_openssl_store_errors();
goto cleanup;
}
}
/* Now sign it */
if (!X509_sign(new_cert, priv_key, req.digest)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "failed to sign it");
goto cleanup;
}
@@ -3143,6 +3473,7 @@ PHP_FUNCTION(openssl_csr_new)
if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
&ext_ctx, req.request_extensions_section, csr))
{
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
} else {
RETVAL_TRUE;
@@ -3151,6 +3482,7 @@ PHP_FUNCTION(openssl_csr_new)
ZVAL_RES(return_value, zend_register_resource(csr, le_csr));
csr = NULL;
} else {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Error signing request");
}
@@ -3170,7 +3502,10 @@ PHP_FUNCTION(openssl_csr_new)
req.priv_key = NULL;
}
}
+ } else {
+ php_openssl_store_errors();
}
+
}
}
if (csr) {
@@ -3229,7 +3564,12 @@ PHP_FUNCTION(openssl_csr_get_public_key)
RETURN_FALSE;
}
- tpubkey=X509_REQ_get_pubkey(csr);
+ tpubkey = X509_REQ_get_pubkey(csr);
+ if (tpubkey == NULL) {
+ php_openssl_store_errors();
+ RETURN_FALSE;
+ }
+
RETURN_RES(zend_register_resource(tpubkey, le_key));
}
/* }}} */
@@ -3364,6 +3704,7 @@ static EVP_PKEY * php_openssl_evp_from_zval(zval * val, int public_key, char * p
in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
}
if (in == NULL) {
+ php_openssl_store_errors();
TMP_CLEAN;
}
key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
@@ -3390,9 +3731,16 @@ static EVP_PKEY * php_openssl_evp_from_zval(zval * val, int public_key, char * p
}
}
- if (public_key && cert && key == NULL) {
- /* extract public key from X509 cert */
- key = (EVP_PKEY *) X509_get_pubkey(cert);
+ if (key == NULL) {
+ php_openssl_store_errors();
+
+ if (public_key && cert) {
+ /* extract public key from X509 cert */
+ key = (EVP_PKEY *) X509_get_pubkey(cert);
+ if (key == NULL) {
+ php_openssl_store_errors();
+ }
+ }
}
if (free_cert && cert) {
@@ -3422,6 +3770,9 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
}
randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE");
+ if (randfile == NULL) {
+ php_openssl_store_errors();
+ }
php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
if ((req->priv_key = EVP_PKEY_new()) != NULL) {
@@ -3443,12 +3794,16 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
}
rsaparam = RSA_new();
PHP_OPENSSL_RAND_ADD_TIME();
- RSA_generate_key_ex(rsaparam, req->priv_key_bits, bne, NULL);
+ if (rsaparam == NULL || !RSA_generate_key_ex(rsaparam, req->priv_key_bits, bne, NULL)) {
+ php_openssl_store_errors();
+ }
BN_free(bne);
}
#endif
if (rsaparam && EVP_PKEY_assign_RSA(req->priv_key, rsaparam)) {
return_val = req->priv_key;
+ } else {
+ php_openssl_store_errors();
}
}
break;
@@ -3467,10 +3822,15 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
if (DSA_generate_key(dsaparam)) {
if (EVP_PKEY_assign_DSA(req->priv_key, dsaparam)) {
return_val = req->priv_key;
+ } else {
+ php_openssl_store_errors();
}
} else {
+ php_openssl_store_errors();
DSA_free(dsaparam);
}
+ } else {
+ php_openssl_store_errors();
}
}
break;
@@ -3491,10 +3851,35 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
if (DH_check(dhparam, &codes) && codes == 0 && DH_generate_key(dhparam)) {
if (EVP_PKEY_assign_DH(req->priv_key, dhparam)) {
return_val = req->priv_key;
+ } else {
+ php_openssl_store_errors();
}
} else {
+ php_openssl_store_errors();
DH_free(dhparam);
}
+ } else {
+ php_openssl_store_errors();
+ }
+ }
+ break;
+#endif
+#ifdef HAVE_EVP_PKEY_EC
+ case OPENSSL_KEYTYPE_EC:
+ {
+ if (req->curve_name == NID_undef) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing configuration value: 'curve_name' not set");
+ return NULL;
+ }
+ EC_KEY *eckey = EC_KEY_new_by_curve_name(req->curve_name);
+ if (eckey) {
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+ if (EC_KEY_generate_key(eckey) &&
+ EVP_PKEY_assign_EC_KEY(req->priv_key, eckey)) {
+ return_val = req->priv_key;
+ } else {
+ EC_KEY_free(eckey);
+ }
}
}
break;
@@ -3502,6 +3887,8 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
default:
php_error_docref(NULL, E_WARNING, "Unsupported private key type");
}
+ } else {
+ php_openssl_store_errors();
}
php_openssl_write_rand_file(randfile, egdsocket, seeded);
@@ -3522,13 +3909,20 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey)
{
assert(pkey != NULL);
- switch (pkey->type) {
+ switch (EVP_PKEY_id(pkey)) {
#ifndef NO_RSA
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
- assert(pkey->pkey.rsa != NULL);
- if (pkey->pkey.rsa != NULL && (NULL == pkey->pkey.rsa->p || NULL == pkey->pkey.rsa->q)) {
- return 0;
+ {
+ RSA *rsa = EVP_PKEY_get0_RSA(pkey);
+ if (rsa != NULL) {
+ const BIGNUM *p, *q;
+
+ RSA_get0_factors(rsa, &p, &q);
+ if (p == NULL || q == NULL) {
+ return 0;
+ }
+ }
}
break;
#endif
@@ -3538,28 +3932,51 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey)
case EVP_PKEY_DSA2:
case EVP_PKEY_DSA3:
case EVP_PKEY_DSA4:
- assert(pkey->pkey.dsa != NULL);
+ {
+ DSA *dsa = EVP_PKEY_get0_DSA(pkey);
+ if (dsa != NULL) {
+ const BIGNUM *p, *q, *g, *pub_key, *priv_key;
+
+ DSA_get0_pqg(dsa, &p, &q, &g);
+ if (p == NULL || q == NULL) {
+ return 0;
+ }
- if (NULL == pkey->pkey.dsa->p || NULL == pkey->pkey.dsa->q || NULL == pkey->pkey.dsa->priv_key){
- return 0;
+ DSA_get0_key(dsa, &pub_key, &priv_key);
+ if (priv_key == NULL) {
+ return 0;
+ }
+ }
}
break;
#endif
#ifndef NO_DH
case EVP_PKEY_DH:
- assert(pkey->pkey.dh != NULL);
+ {
+ DH *dh = EVP_PKEY_get0_DH(pkey);
+ if (dh != NULL) {
+ const BIGNUM *p, *q, *g, *pub_key, *priv_key;
+
+ DH_get0_pqg(dh, &p, &q, &g);
+ if (p == NULL) {
+ return 0;
+ }
- if (NULL == pkey->pkey.dh->p || NULL == pkey->pkey.dh->priv_key) {
- return 0;
+ DH_get0_key(dh, &pub_key, &priv_key);
+ if (priv_key == NULL) {
+ return 0;
+ }
+ }
}
break;
#endif
#ifdef HAVE_EVP_PKEY_EC
case EVP_PKEY_EC:
- assert(pkey->pkey.ec != NULL);
-
- if ( NULL == EC_KEY_get0_private_key(pkey->pkey.ec)) {
- return 0;
+ {
+ EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+ if (ec != NULL && NULL == EC_KEY_get0_private_key(ec)) {
+ return 0;
+ }
}
break;
#endif
@@ -3571,42 +3988,93 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey)
}
/* }}} */
-#define OPENSSL_PKEY_GET_BN(_type, _name) do { \
- if (pkey->pkey._type->_name != NULL) { \
- int len = BN_num_bytes(pkey->pkey._type->_name); \
- zend_string *str = zend_string_alloc(len, 0); \
- BN_bn2bin(pkey->pkey._type->_name, (unsigned char*)ZSTR_VAL(str)); \
- ZSTR_VAL(str)[len] = 0; \
- add_assoc_str(&_type, #_name, str); \
- } \
- } while (0)
-
-#define OPENSSL_PKEY_SET_BN(_ht, _type, _name) do { \
- zval *bn; \
- if ((bn = zend_hash_str_find(_ht, #_name, sizeof(#_name)-1)) != NULL && \
- Z_TYPE_P(bn) == IS_STRING) { \
- _type->_name = BN_bin2bn( \
- (unsigned char*)Z_STRVAL_P(bn), \
- (int)Z_STRLEN_P(bn), NULL); \
- } \
+#define OPENSSL_GET_BN(_array, _bn, _name) do { \
+ if (_bn != NULL) { \
+ int len = BN_num_bytes(_bn); \
+ zend_string *str = zend_string_alloc(len, 0); \
+ BN_bn2bin(_bn, (unsigned char*)ZSTR_VAL(str)); \
+ ZSTR_VAL(str)[len] = 0; \
+ add_assoc_str(&_array, #_name, str); \
+ } \
+ } while (0);
+
+#define OPENSSL_PKEY_GET_BN(_type, _name) OPENSSL_GET_BN(_type, _name, _name)
+
+#define OPENSSL_PKEY_SET_BN(_data, _name) do { \
+ zval *bn; \
+ if ((bn = zend_hash_str_find(Z_ARRVAL_P(_data), #_name, sizeof(#_name)-1)) != NULL && \
+ Z_TYPE_P(bn) == IS_STRING) { \
+ _name = BN_bin2bn( \
+ (unsigned char*)Z_STRVAL_P(bn), \
+ (int)Z_STRLEN_P(bn), NULL); \
+ } else { \
+ _name = NULL; \
+ } \
} while (0);
+/* {{{ php_openssl_pkey_init_rsa */
+zend_bool php_openssl_pkey_init_and_assign_rsa(EVP_PKEY *pkey, RSA *rsa, zval *data)
+{
+ BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
+
+ OPENSSL_PKEY_SET_BN(data, n);
+ OPENSSL_PKEY_SET_BN(data, e);
+ OPENSSL_PKEY_SET_BN(data, d);
+ if (!n || !d || !RSA_set0_key(rsa, n, e, d)) {
+ return 0;
+ }
+
+ OPENSSL_PKEY_SET_BN(data, p);
+ OPENSSL_PKEY_SET_BN(data, q);
+ if ((p || q) && !RSA_set0_factors(rsa, p, q)) {
+ return 0;
+ }
+
+ OPENSSL_PKEY_SET_BN(data, dmp1);
+ OPENSSL_PKEY_SET_BN(data, dmq1);
+ OPENSSL_PKEY_SET_BN(data, iqmp);
+ if ((dmp1 || dmq1 || iqmp) && !RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp)) {
+ return 0;
+ }
+
+ if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
+ php_openssl_store_errors();
+ return 0;
+ }
+
+ return 1;
+}
+
/* {{{ php_openssl_pkey_init_dsa */
-zend_bool php_openssl_pkey_init_dsa(DSA *dsa)
+zend_bool php_openssl_pkey_init_dsa(DSA *dsa, zval *data)
{
- if (!dsa->p || !dsa->q || !dsa->g) {
+ BIGNUM *p, *q, *g, *priv_key, *pub_key;
+ const BIGNUM *priv_key_const, *pub_key_const;
+
+ OPENSSL_PKEY_SET_BN(data, p);
+ OPENSSL_PKEY_SET_BN(data, q);
+ OPENSSL_PKEY_SET_BN(data, g);
+ if (!p || !q || !g || !DSA_set0_pqg(dsa, p, q, g)) {
return 0;
}
- if (dsa->priv_key || dsa->pub_key) {
- return 1;
+
+ OPENSSL_PKEY_SET_BN(data, pub_key);
+ OPENSSL_PKEY_SET_BN(data, priv_key);
+ if (pub_key) {
+ return DSA_set0_key(dsa, pub_key, priv_key);
}
+
+ /* generate key */
PHP_OPENSSL_RAND_ADD_TIME();
if (!DSA_generate_key(dsa)) {
+ php_openssl_store_errors();
return 0;
}
+
/* if BN_mod_exp return -1, then DSA_generate_key succeed for failed key
* so we need to double check that public key is created */
- if (!dsa->pub_key || BN_is_zero(dsa->pub_key)) {
+ DSA_get0_key(dsa, &pub_key_const, &priv_key_const);
+ if (!pub_key_const || BN_is_zero(pub_key_const)) {
return 0;
}
/* all good */
@@ -3615,16 +4083,27 @@ zend_bool php_openssl_pkey_init_dsa(DSA *dsa)
/* }}} */
/* {{{ php_openssl_pkey_init_dh */
-zend_bool php_openssl_pkey_init_dh(DH *dh)
+zend_bool php_openssl_pkey_init_dh(DH *dh, zval *data)
{
- if (!dh->p || !dh->g) {
+ BIGNUM *p, *q, *g, *priv_key, *pub_key;
+
+ OPENSSL_PKEY_SET_BN(data, p);
+ OPENSSL_PKEY_SET_BN(data, q);
+ OPENSSL_PKEY_SET_BN(data, g);
+ if (!p || !g || !DH_set0_pqg(dh, p, q, g)) {
return 0;
}
- if (dh->pub_key) {
- return 1;
+
+ OPENSSL_PKEY_SET_BN(data, priv_key);
+ OPENSSL_PKEY_SET_BN(data, pub_key);
+ if (pub_key) {
+ return DH_set0_key(dh, pub_key, priv_key);
}
+
+ /* generate key */
PHP_OPENSSL_RAND_ADD_TIME();
if (!DH_generate_key(dh)) {
+ php_openssl_store_errors();
return 0;
}
/* all good */
@@ -3654,22 +4133,16 @@ PHP_FUNCTION(openssl_pkey_new)
if (pkey) {
RSA *rsa = RSA_new();
if (rsa) {
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, n);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, e);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, d);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, p);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, q);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, dmp1);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, dmq1);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, iqmp);
- if (rsa->n && rsa->d) {
- if (EVP_PKEY_assign_RSA(pkey, rsa)) {
- RETURN_RES(zend_register_resource(pkey, le_key));
- }
+ if (php_openssl_pkey_init_and_assign_rsa(pkey, rsa, data)) {
+ RETURN_RES(zend_register_resource(pkey, le_key));
}
RSA_free(rsa);
+ } else {
+ php_openssl_store_errors();
}
EVP_PKEY_free(pkey);
+ } else {
+ php_openssl_store_errors();
}
RETURN_FALSE;
} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa") - 1)) != NULL &&
@@ -3678,19 +4151,20 @@ PHP_FUNCTION(openssl_pkey_new)
if (pkey) {
DSA *dsa = DSA_new();
if (dsa) {
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, p);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, q);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, g);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, priv_key);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, pub_key);
- if (php_openssl_pkey_init_dsa(dsa)) {
+ if (php_openssl_pkey_init_dsa(dsa, data)) {
if (EVP_PKEY_assign_DSA(pkey, dsa)) {
RETURN_RES(zend_register_resource(pkey, le_key));
+ } else {
+ php_openssl_store_errors();
}
}
DSA_free(dsa);
+ } else {
+ php_openssl_store_errors();
}
EVP_PKEY_free(pkey);
+ } else {
+ php_openssl_store_errors();
}
RETURN_FALSE;
} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dh", sizeof("dh") - 1)) != NULL &&
@@ -3699,21 +4173,135 @@ PHP_FUNCTION(openssl_pkey_new)
if (pkey) {
DH *dh = DH_new();
if (dh) {
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dh, p);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dh, g);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dh, priv_key);
- OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dh, pub_key);
- if (php_openssl_pkey_init_dh(dh)) {
+ if (php_openssl_pkey_init_dh(dh, data)) {
if (EVP_PKEY_assign_DH(pkey, dh)) {
ZVAL_COPY_VALUE(return_value, zend_list_insert(pkey, le_key));
return;
+ } else {
+ php_openssl_store_errors();
}
}
DH_free(dh);
+ } else {
+ php_openssl_store_errors();
+ }
+ EVP_PKEY_free(pkey);
+ } else {
+ php_openssl_store_errors();
+ }
+ RETURN_FALSE;
+#ifdef HAVE_EVP_PKEY_EC
+ } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "ec", sizeof("ec") - 1)) != NULL &&
+ Z_TYPE_P(data) == IS_ARRAY) {
+ EC_KEY *eckey = NULL;
+ EC_GROUP *group = NULL;
+ EC_POINT *pnt = NULL;
+ const BIGNUM *d;
+ pkey = EVP_PKEY_new();
+ if (pkey) {
+ eckey = EC_KEY_new();
+ if (eckey) {
+ EC_GROUP *group = NULL;
+ zval *bn;
+ zval *x;
+ zval *y;
+
+ if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1)) != NULL &&
+ Z_TYPE_P(bn) == IS_STRING) {
+ int nid = OBJ_sn2nid(Z_STRVAL_P(bn));
+ if (nid != NID_undef) {
+ group = EC_GROUP_new_by_curve_name(nid);
+ if (!group) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+ EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
+ if (!EC_KEY_set_group(eckey, group)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ }
+ }
+
+ if (group == NULL) {
+ php_error_docref(NULL, E_WARNING, "Unknown curve_name");
+ goto clean_exit;
+ }
+
+ // The public key 'pnt' can be calculated from 'd' or is defined by 'x' and 'y'
+ if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "d", sizeof("d") - 1)) != NULL &&
+ Z_TYPE_P(bn) == IS_STRING) {
+ d = BN_bin2bn((unsigned char*) Z_STRVAL_P(bn), Z_STRLEN_P(bn), NULL);
+ if (!EC_KEY_set_private_key(eckey, d)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ // Calculate the public key by multiplying the Point Q with the public key
+ // P = d * Q
+ pnt = EC_POINT_new(group);
+ if (!pnt || !EC_POINT_mul(group, pnt, d, NULL, NULL, NULL)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ } else if ((x = zend_hash_str_find(Z_ARRVAL_P(data), "x", sizeof("x") - 1)) != NULL &&
+ Z_TYPE_P(x) == IS_STRING &&
+ (y = zend_hash_str_find(Z_ARRVAL_P(data), "y", sizeof("y") - 1)) != NULL &&
+ Z_TYPE_P(y) == IS_STRING) {
+ pnt = EC_POINT_new(group);
+ if (pnt == NULL) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ if (!EC_POINT_set_affine_coordinates_GFp(
+ group, pnt, BN_bin2bn((unsigned char*) Z_STRVAL_P(x), Z_STRLEN_P(x), NULL),
+ BN_bin2bn((unsigned char*) Z_STRVAL_P(y), Z_STRLEN_P(y), NULL), NULL)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ }
+
+ if (pnt != NULL) {
+ if (!EC_KEY_set_public_key(eckey, pnt)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ EC_POINT_free(pnt);
+ pnt = NULL;
+ }
+
+ if (!EC_KEY_check_key(eckey)) {
+ PHP_OPENSSL_RAND_ADD_TIME();
+ EC_KEY_generate_key(eckey);
+ php_openssl_store_errors();
+ }
+ if (EC_KEY_check_key(eckey) && EVP_PKEY_assign_EC_KEY(pkey, eckey)) {
+ EC_GROUP_free(group);
+ RETURN_RES(zend_register_resource(pkey, le_key));
+ } else {
+ php_openssl_store_errors();
+ }
+ } else {
+ php_openssl_store_errors();
}
+ } else {
+ php_openssl_store_errors();
+ }
+clean_exit:
+ if (pnt != NULL) {
+ EC_POINT_free(pnt);
+ }
+ if (group != NULL) {
+ EC_GROUP_free(group);
+ }
+ if (eckey != NULL) {
+ EC_KEY_free(eckey);
+ }
+ if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
RETURN_FALSE;
+#endif
}
}
@@ -3770,6 +4358,10 @@ PHP_FUNCTION(openssl_pkey_export_to_file)
if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
bio_out = BIO_new_file(filename, "w");
+ if (bio_out == NULL) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
if (passphrase && req.priv_key_encrypt) {
if (req.priv_key_encrypt_cipher) {
@@ -3781,7 +4373,7 @@ PHP_FUNCTION(openssl_pkey_export_to_file)
cipher = NULL;
}
- switch (EVP_PKEY_type(key->type)) {
+ switch (EVP_PKEY_base_id(key)) {
#ifdef HAVE_EVP_PKEY_EC
case EVP_PKEY_EC:
pem_write = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get1_EC_KEY(key), cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
@@ -3796,8 +4388,12 @@ PHP_FUNCTION(openssl_pkey_export_to_file)
/* Success!
* If returning the output as a string, do so now */
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
}
+
+clean_exit:
PHP_SSL_REQ_DISPOSE(&req);
if (key_resource == NULL && key) {
@@ -3851,7 +4447,7 @@ PHP_FUNCTION(openssl_pkey_export)
cipher = NULL;
}
- switch (EVP_PKEY_type(key->type)) {
+ switch (EVP_PKEY_base_id(key)) {
#ifdef HAVE_EVP_PKEY_EC
case EVP_PKEY_EC:
pem_write = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get1_EC_KEY(key), cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
@@ -3873,6 +4469,8 @@ PHP_FUNCTION(openssl_pkey_export)
bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
zval_dtor(out);
ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len);
+ } else {
+ php_openssl_store_errors();
}
}
PHP_SSL_REQ_DISPOSE(&req);
@@ -3965,7 +4563,11 @@ PHP_FUNCTION(openssl_pkey_get_details)
RETURN_FALSE;
}
out = BIO_new(BIO_s_mem());
- PEM_write_bio_PUBKEY(out, pkey);
+ if (!PEM_write_bio_PUBKEY(out, pkey)) {
+ BIO_free(out);
+ php_openssl_store_errors();
+ RETURN_FALSE;
+ }
pbio_len = BIO_get_mem_data(out, &pbio);
array_init(return_value);
@@ -3974,74 +4576,98 @@ PHP_FUNCTION(openssl_pkey_get_details)
/*TODO: Use the real values once the openssl constants are used
* See the enum at the top of this file
*/
- switch (EVP_PKEY_type(pkey->type)) {
+ switch (EVP_PKEY_base_id(pkey)) {
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
- ktype = OPENSSL_KEYTYPE_RSA;
-
- if (pkey->pkey.rsa != NULL) {
- zval rsa;
-
- array_init(&rsa);
- OPENSSL_PKEY_GET_BN(rsa, n);
- OPENSSL_PKEY_GET_BN(rsa, e);
- OPENSSL_PKEY_GET_BN(rsa, d);
- OPENSSL_PKEY_GET_BN(rsa, p);
- OPENSSL_PKEY_GET_BN(rsa, q);
- OPENSSL_PKEY_GET_BN(rsa, dmp1);
- OPENSSL_PKEY_GET_BN(rsa, dmq1);
- OPENSSL_PKEY_GET_BN(rsa, iqmp);
- add_assoc_zval(return_value, "rsa", &rsa);
+ {
+ RSA *rsa = EVP_PKEY_get0_RSA(pkey);
+ ktype = OPENSSL_KEYTYPE_RSA;
+
+ if (rsa != NULL) {
+ zval z_rsa;
+ const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
+
+ RSA_get0_key(rsa, &n, &e, &d);
+ RSA_get0_factors(rsa, &p, &q);
+ RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+
+ array_init(&z_rsa);
+ OPENSSL_PKEY_GET_BN(z_rsa, n);
+ OPENSSL_PKEY_GET_BN(z_rsa, e);
+ OPENSSL_PKEY_GET_BN(z_rsa, d);
+ OPENSSL_PKEY_GET_BN(z_rsa, p);
+ OPENSSL_PKEY_GET_BN(z_rsa, q);
+ OPENSSL_PKEY_GET_BN(z_rsa, dmp1);
+ OPENSSL_PKEY_GET_BN(z_rsa, dmq1);
+ OPENSSL_PKEY_GET_BN(z_rsa, iqmp);
+ add_assoc_zval(return_value, "rsa", &z_rsa);
+ }
}
-
break;
case EVP_PKEY_DSA:
case EVP_PKEY_DSA2:
case EVP_PKEY_DSA3:
case EVP_PKEY_DSA4:
- ktype = OPENSSL_KEYTYPE_DSA;
-
- if (pkey->pkey.dsa != NULL) {
- zval dsa;
-
- array_init(&dsa);
- OPENSSL_PKEY_GET_BN(dsa, p);
- OPENSSL_PKEY_GET_BN(dsa, q);
- OPENSSL_PKEY_GET_BN(dsa, g);
- OPENSSL_PKEY_GET_BN(dsa, priv_key);
- OPENSSL_PKEY_GET_BN(dsa, pub_key);
- add_assoc_zval(return_value, "dsa", &dsa);
+ {
+ DSA *dsa = EVP_PKEY_get0_DSA(pkey);
+ ktype = OPENSSL_KEYTYPE_DSA;
+
+ if (dsa != NULL) {
+ zval z_dsa;
+ const BIGNUM *p, *q, *g, *priv_key, *pub_key;
+
+ DSA_get0_pqg(dsa, &p, &q, &g);
+ DSA_get0_key(dsa, &pub_key, &priv_key);
+
+ array_init(&z_dsa);
+ OPENSSL_PKEY_GET_BN(z_dsa, p);
+ OPENSSL_PKEY_GET_BN(z_dsa, q);
+ OPENSSL_PKEY_GET_BN(z_dsa, g);
+ OPENSSL_PKEY_GET_BN(z_dsa, priv_key);
+ OPENSSL_PKEY_GET_BN(z_dsa, pub_key);
+ add_assoc_zval(return_value, "dsa", &z_dsa);
+ }
}
break;
case EVP_PKEY_DH:
-
- ktype = OPENSSL_KEYTYPE_DH;
-
- if (pkey->pkey.dh != NULL) {
- zval dh;
-
- array_init(&dh);
- OPENSSL_PKEY_GET_BN(dh, p);
- OPENSSL_PKEY_GET_BN(dh, g);
- OPENSSL_PKEY_GET_BN(dh, priv_key);
- OPENSSL_PKEY_GET_BN(dh, pub_key);
- add_assoc_zval(return_value, "dh", &dh);
+ {
+ DH *dh = EVP_PKEY_get0_DH(pkey);
+ ktype = OPENSSL_KEYTYPE_DH;
+
+ if (dh != NULL) {
+ zval z_dh;
+ const BIGNUM *p, *q, *g, *priv_key, *pub_key;
+
+ DH_get0_pqg(dh, &p, &q, &g);
+ DH_get0_key(dh, &pub_key, &priv_key);
+
+ array_init(&z_dh);
+ OPENSSL_PKEY_GET_BN(z_dh, p);
+ OPENSSL_PKEY_GET_BN(z_dh, g);
+ OPENSSL_PKEY_GET_BN(z_dh, priv_key);
+ OPENSSL_PKEY_GET_BN(z_dh, pub_key);
+ add_assoc_zval(return_value, "dh", &z_dh);
+ }
}
-
break;
#ifdef HAVE_EVP_PKEY_EC
case EVP_PKEY_EC:
ktype = OPENSSL_KEYTYPE_EC;
- if (pkey->pkey.ec != NULL) {
+ if (EVP_PKEY_get0_EC_KEY(pkey) != NULL) {
zval ec;
const EC_GROUP *ec_group;
+ const EC_POINT *pub;
int nid;
char *crv_sn;
ASN1_OBJECT *obj;
// openssl recommends a buffer length of 80
char oir_buf[80];
+ const EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pkey);
+ BIGNUM *x = BN_new();
+ BIGNUM *y = BN_new();
+ const BIGNUM *d;
- ec_group = EC_KEY_get0_group(EVP_PKEY_get1_EC_KEY(pkey));
+ ec_group = EC_KEY_get0_group(ec_key);
// Curve nid (numerical identifier) used for ASN1 mapping
nid = EC_GROUP_get_curve_name(ec_group);
@@ -4059,11 +4685,27 @@ PHP_FUNCTION(openssl_pkey_get_details)
obj = OBJ_nid2obj(nid);
if (obj != NULL) {
int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
- add_assoc_stringl(&ec, "curve_oid", (char*)oir_buf, oir_len);
+ add_assoc_stringl(&ec, "curve_oid", (char*) oir_buf, oir_len);
ASN1_OBJECT_free(obj);
}
+ pub = EC_KEY_get0_public_key(ec_key);
+
+ if (EC_POINT_get_affine_coordinates_GFp(ec_group, pub, x, y, NULL)) {
+ OPENSSL_GET_BN(ec, x, x);
+ OPENSSL_GET_BN(ec, y, y);
+ } else {
+ php_openssl_store_errors();
+ }
+
+ if ((d = EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(pkey))) != NULL) {
+ OPENSSL_GET_BN(ec, d, d);
+ }
+
add_assoc_zval(return_value, "ec", &ec);
+
+ BN_free(x);
+ BN_free(y);
}
break;
#endif
@@ -4077,9 +4719,54 @@ PHP_FUNCTION(openssl_pkey_get_details)
}
/* }}} */
+/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
+ Computes shared secret for public value of remote DH key and local DH key */
+PHP_FUNCTION(openssl_dh_compute_key)
+{
+ zval *key;
+ char *pub_str;
+ size_t pub_len;
+ DH *dh;
+ EVP_PKEY *pkey;
+ BIGNUM *pub;
+ zend_string *data;
+ int len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sr", &pub_str, &pub_len, &key) == FAILURE) {
+ return;
+ }
+ if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
+ RETURN_FALSE;
+ }
+ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) {
+ RETURN_FALSE;
+ }
+ dh = EVP_PKEY_get0_DH(pkey);
+ if (dh == NULL) {
+ RETURN_FALSE;
+ }
+
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(pub_len, pub_key);
+ pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
+
+ data = zend_string_alloc(DH_size(dh), 0);
+ len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, dh);
+
+ if (len >= 0) {
+ ZSTR_LEN(data) = len;
+ ZSTR_VAL(data)[len] = 0;
+ RETVAL_STR(data);
+ } else {
+ php_openssl_store_errors();
+ zend_string_release(data);
+ RETVAL_FALSE;
+ }
+
+ BN_free(pub);
+}
/* }}} */
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+/* }}} */
/* {{{ proto string openssl_pbkdf2(string password, string salt, long key_length, long iterations [, string digest_method = "sha1"])
Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
@@ -4130,14 +4817,13 @@ PHP_FUNCTION(openssl_pbkdf2)
ZSTR_VAL(out_buffer)[key_length] = 0;
RETURN_NEW_STR(out_buffer);
} else {
+ php_openssl_store_errors();
zend_string_release(out_buffer);
RETURN_FALSE;
}
}
/* }}} */
-#endif
-
/* {{{ PKCS7 S/MIME functions */
/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
@@ -4188,6 +4874,7 @@ PHP_FUNCTION(openssl_pkcs7_verify)
in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r");
if (in == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
p7 = SMIME_read_PKCS7(in, &datain);
@@ -4195,6 +4882,7 @@ PHP_FUNCTION(openssl_pkcs7_verify)
#if DEBUG_SMIME
zend_printf("SMIME_read_PKCS7 failed\n");
#endif
+ php_openssl_store_errors();
goto clean_exit;
}
@@ -4206,6 +4894,7 @@ PHP_FUNCTION(openssl_pkcs7_verify)
dataout = BIO_new_file(datafilename, "w");
if (dataout == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
}
@@ -4228,19 +4917,31 @@ PHP_FUNCTION(openssl_pkcs7_verify)
if (certout) {
int i;
signers = PKCS7_get0_signers(p7, NULL, (int)flags);
+ if (signers != NULL) {
- for(i = 0; i < sk_X509_num(signers); i++) {
- PEM_write_bio_X509(certout, sk_X509_value(signers, i));
+ for (i = 0; i < sk_X509_num(signers); i++) {
+ if (!PEM_write_bio_X509(certout, sk_X509_value(signers, i))) {
+ php_openssl_store_errors();
+ RETVAL_LONG(-1);
+ php_error_docref(NULL, E_WARNING, "failed to write signer %d", i);
+ }
+ }
+
+ sk_X509_free(signers);
+ } else {
+ RETVAL_LONG(-1);
+ php_openssl_store_errors();
}
+
BIO_free(certout);
- sk_X509_free(signers);
} else {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
RETVAL_LONG(-1);
}
}
- goto clean_exit;
} else {
+ php_openssl_store_errors();
RETVAL_FALSE;
}
clean_exit:
@@ -4285,11 +4986,13 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
infile = BIO_new_file(infilename, "r");
if (infile == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
outfile = BIO_new_file(outfilename, "w");
if (outfile == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
@@ -4310,6 +5013,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
make a copy and push that on the stack instead */
cert = X509_dup(cert);
if (cert == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
}
@@ -4329,6 +5033,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
make a copy and push that on the stack instead */
cert = X509_dup(cert);
if (cert == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
}
@@ -4346,6 +5051,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, (int)flags);
if (p7 == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
@@ -4365,7 +5071,10 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
(void)BIO_reset(infile);
/* write the encrypted data */
- SMIME_write_PKCS7(outfile, p7, infile, (int)flags);
+ if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
RETVAL_TRUE;
@@ -4435,18 +5144,21 @@ PHP_FUNCTION(openssl_pkcs7_sign)
infile = BIO_new_file(infilename, "r");
if (infile == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening input file %s!", infilename);
goto clean_exit;
}
outfile = BIO_new_file(outfilename, "w");
if (outfile == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening output file %s!", outfilename);
goto clean_exit;
}
p7 = PKCS7_sign(cert, privkey, others, infile, (int)flags);
if (p7 == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error creating PKCS7 structure!");
goto clean_exit;
}
@@ -4455,18 +5167,26 @@ PHP_FUNCTION(openssl_pkcs7_sign)
/* tack on extra headers */
if (zheaders) {
+ int ret;
+
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) {
convert_to_string_ex(hval);
if (strindex) {
- BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), Z_STRVAL_P(hval));
+ ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), Z_STRVAL_P(hval));
} else {
- BIO_printf(outfile, "%s\n", Z_STRVAL_P(hval));
+ ret = BIO_printf(outfile, "%s\n", Z_STRVAL_P(hval));
+ }
+ if (ret < 0) {
+ php_openssl_store_errors();
}
} ZEND_HASH_FOREACH_END();
}
/* write the signed data */
- SMIME_write_PKCS7(outfile, p7, infile, (int)flags);
+ if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
RETVAL_TRUE;
@@ -4527,20 +5247,25 @@ PHP_FUNCTION(openssl_pkcs7_decrypt)
in = BIO_new_file(infilename, "r");
if (in == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
out = BIO_new_file(outfilename, "w");
if (out == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
p7 = SMIME_read_PKCS7(in, &datain);
if (p7 == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
clean_exit:
PKCS7_free(p7);
@@ -4589,13 +5314,13 @@ PHP_FUNCTION(openssl_private_encrypt)
cryptedlen = EVP_PKEY_size(pkey);
cryptedbuf = zend_string_alloc(cryptedlen, 0);
- switch (pkey->type) {
+ switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
successful = (RSA_private_encrypt((int)data_len,
(unsigned char *)data,
(unsigned char *)ZSTR_VAL(cryptedbuf),
- pkey->pkey.rsa,
+ EVP_PKEY_get0_RSA(pkey),
(int)padding) == cryptedlen);
break;
default:
@@ -4608,6 +5333,8 @@ PHP_FUNCTION(openssl_private_encrypt)
ZVAL_NEW_STR(crypted, cryptedbuf);
cryptedbuf = NULL;
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (cryptedbuf) {
zend_string_release(cryptedbuf);
@@ -4649,13 +5376,13 @@ PHP_FUNCTION(openssl_private_decrypt)
cryptedlen = EVP_PKEY_size(pkey);
crypttemp = emalloc(cryptedlen + 1);
- switch (pkey->type) {
+ switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
cryptedlen = RSA_private_decrypt((int)data_len,
(unsigned char *)data,
crypttemp,
- pkey->pkey.rsa,
+ EVP_PKEY_get0_RSA(pkey),
(int)padding);
if (cryptedlen != -1) {
cryptedbuf = zend_string_alloc(cryptedlen, 0);
@@ -4675,6 +5402,8 @@ PHP_FUNCTION(openssl_private_decrypt)
ZVAL_NEW_STR(crypted, cryptedbuf);
cryptedbuf = NULL;
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (keyresource == NULL) {
@@ -4715,13 +5444,13 @@ PHP_FUNCTION(openssl_public_encrypt)
cryptedlen = EVP_PKEY_size(pkey);
cryptedbuf = zend_string_alloc(cryptedlen, 0);
- switch (pkey->type) {
+ switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
successful = (RSA_public_encrypt((int)data_len,
(unsigned char *)data,
(unsigned char *)ZSTR_VAL(cryptedbuf),
- pkey->pkey.rsa,
+ EVP_PKEY_get0_RSA(pkey),
(int)padding) == cryptedlen);
break;
default:
@@ -4735,6 +5464,8 @@ PHP_FUNCTION(openssl_public_encrypt)
ZVAL_NEW_STR(crypted, cryptedbuf);
cryptedbuf = NULL;
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (keyresource == NULL) {
EVP_PKEY_free(pkey);
@@ -4776,13 +5507,13 @@ PHP_FUNCTION(openssl_public_decrypt)
cryptedlen = EVP_PKEY_size(pkey);
crypttemp = emalloc(cryptedlen + 1);
- switch (pkey->type) {
+ switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
cryptedlen = RSA_public_decrypt((int)data_len,
(unsigned char *)data,
crypttemp,
- pkey->pkey.rsa,
+ EVP_PKEY_get0_RSA(pkey),
(int)padding);
if (cryptedlen != -1) {
cryptedbuf = zend_string_alloc(cryptedlen, 0);
@@ -4804,6 +5535,8 @@ PHP_FUNCTION(openssl_public_decrypt)
ZVAL_NEW_STR(crypted, cryptedbuf);
cryptedbuf = NULL;
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (cryptedbuf) {
@@ -4819,16 +5552,25 @@ PHP_FUNCTION(openssl_public_decrypt)
Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */
PHP_FUNCTION(openssl_error_string)
{
- char buf[512];
+ char buf[256];
unsigned long val;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
- val = ERR_get_error();
+ php_openssl_store_errors();
+
+ if (OPENSSL_G(errors) == NULL || OPENSSL_G(errors)->top == OPENSSL_G(errors)->bottom) {
+ RETURN_FALSE;
+ }
+
+ OPENSSL_G(errors)->bottom = (OPENSSL_G(errors)->bottom + 1) % ERR_NUM_ERRORS;
+ val = OPENSSL_G(errors)->buffer[OPENSSL_G(errors)->bottom];
+
if (val) {
- RETURN_STRING(ERR_error_string(val, buf));
+ ERR_error_string_n(val, buf, 256);
+ RETURN_STRING(buf);
} else {
RETURN_FALSE;
}
@@ -4846,7 +5588,7 @@ PHP_FUNCTION(openssl_sign)
zend_resource *keyresource = NULL;
char * data;
size_t data_len;
- EVP_MD_CTX md_ctx;
+ EVP_MD_CTX *md_ctx;
zval *method = NULL;
zend_long signature_algo = OPENSSL_ALGO_SHA1;
const EVP_MD *mdtype;
@@ -4879,19 +5621,22 @@ PHP_FUNCTION(openssl_sign)
siglen = EVP_PKEY_size(pkey);
sigbuf = zend_string_alloc(siglen, 0);
- EVP_SignInit(&md_ctx, mdtype);
- EVP_SignUpdate(&md_ctx, data, data_len);
- if (EVP_SignFinal (&md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, pkey)) {
+ md_ctx = EVP_MD_CTX_create();
+ if (md_ctx != NULL &&
+ EVP_SignInit(md_ctx, mdtype) &&
+ EVP_SignUpdate(md_ctx, data, data_len) &&
+ EVP_SignFinal(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, pkey)) {
zval_dtor(signature);
ZSTR_VAL(sigbuf)[siglen] = '\0';
ZSTR_LEN(sigbuf) = siglen;
ZVAL_NEW_STR(signature, sigbuf);
RETVAL_TRUE;
} else {
+ php_openssl_store_errors();
efree(sigbuf);
RETVAL_FALSE;
}
- EVP_MD_CTX_cleanup(&md_ctx);
+ EVP_MD_CTX_destroy(md_ctx);
if (keyresource == NULL) {
EVP_PKEY_free(pkey);
}
@@ -4904,8 +5649,8 @@ PHP_FUNCTION(openssl_verify)
{
zval *key;
EVP_PKEY *pkey;
- int err;
- EVP_MD_CTX md_ctx;
+ int err = 0;
+ EVP_MD_CTX *md_ctx;
const EVP_MD *mdtype;
zend_resource *keyresource = NULL;
char * data;
@@ -4943,10 +5688,14 @@ PHP_FUNCTION(openssl_verify)
RETURN_FALSE;
}
- EVP_VerifyInit (&md_ctx, mdtype);
- EVP_VerifyUpdate (&md_ctx, data, data_len);
- err = EVP_VerifyFinal(&md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey);
- EVP_MD_CTX_cleanup(&md_ctx);
+ md_ctx = EVP_MD_CTX_create();
+ if (md_ctx == NULL ||
+ !EVP_VerifyInit (md_ctx, mdtype) ||
+ !EVP_VerifyUpdate (md_ctx, data, data_len) ||
+ (err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey)) < 0) {
+ php_openssl_store_errors();
+ }
+ EVP_MD_CTX_destroy(md_ctx);
if (keyresource == NULL) {
EVP_PKEY_free(pkey);
@@ -4970,7 +5719,7 @@ PHP_FUNCTION(openssl_seal)
char *method =NULL;
size_t method_len = 0;
const EVP_CIPHER *cipher;
- EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX *ctx;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z/a/|sz/", &data, &data_len,
&sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) {
@@ -5023,22 +5772,25 @@ PHP_FUNCTION(openssl_seal)
i++;
} ZEND_HASH_FOREACH_END();
- if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) {
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) {
+ EVP_CIPHER_CTX_free(ctx);
+ php_openssl_store_errors();
RETVAL_FALSE;
- EVP_CIPHER_CTX_cleanup(&ctx);
goto clean_exit;
}
/* allocate one byte extra to make room for \0 */
- buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx));
- EVP_CIPHER_CTX_cleanup(&ctx);
+ buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(ctx));
+ EVP_CIPHER_CTX_cleanup(ctx);
- if (!EVP_SealInit(&ctx, cipher, eks, eksl, &iv_buf[0], pkeys, nkeys) ||
- !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
- !EVP_SealFinal(&ctx, buf + len1, &len2)) {
- RETVAL_FALSE;
+ if (!EVP_SealInit(ctx, cipher, eks, eksl, &iv_buf[0], pkeys, nkeys) ||
+ !EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
+ !EVP_SealFinal(ctx, buf + len1, &len2)) {
efree(buf);
- EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_CIPHER_CTX_free(ctx);
+ php_openssl_store_errors();
+ RETVAL_FALSE;
goto clean_exit;
}
@@ -5066,7 +5818,7 @@ PHP_FUNCTION(openssl_seal)
efree(buf);
}
RETVAL_LONG(len1 + len2);
- EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_CIPHER_CTX_free(ctx);
clean_exit:
for (i=0; i<nkeys; i++) {
@@ -5093,7 +5845,7 @@ PHP_FUNCTION(openssl_open)
int len1, len2, cipher_iv_len;
unsigned char *buf, *iv_buf;
zend_resource *keyresource = NULL;
- EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX *ctx;
char * data;
size_t data_len;
char * ekey;
@@ -5133,7 +5885,7 @@ PHP_FUNCTION(openssl_open)
"Cipher algorithm requires an IV to be supplied as a sixth parameter");
RETURN_FALSE;
}
- if (cipher_iv_len != iv_len) {
+ if ((size_t)cipher_iv_len != iv_len) {
php_error_docref(NULL, E_WARNING, "IV length is invalid");
RETURN_FALSE;
}
@@ -5144,26 +5896,24 @@ PHP_FUNCTION(openssl_open)
buf = emalloc(data_len + 1);
- if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) &&
- EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, (int)data_len)) {
- if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) {
- efree(buf);
- RETVAL_FALSE;
- } else {
- zval_dtor(opendata);
- buf[len1 + len2] = '\0';
- ZVAL_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
- efree(buf);
- RETVAL_TRUE;
- }
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx != NULL && EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) &&
+ EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) &&
+ EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) {
+ zval_dtor(opendata);
+ buf[len1 + len2] = '\0';
+ ZVAL_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
+ RETVAL_TRUE;
} else {
- efree(buf);
+ php_openssl_store_errors();
RETVAL_FALSE;
}
+
+ efree(buf);
if (keyresource == NULL) {
EVP_PKEY_free(pkey);
}
- EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_CIPHER_CTX_free(ctx);
}
/* }}} */
@@ -5213,6 +5963,33 @@ PHP_FUNCTION(openssl_get_cipher_methods)
}
/* }}} */
+/* {{{ proto array openssl_get_curve_names()
+ Return array of available elliptic curves */
+#ifdef HAVE_EVP_PKEY_EC
+PHP_FUNCTION(openssl_get_curve_names)
+{
+ EC_builtin_curve *curves = NULL;
+ const char *sname;
+ size_t i;
+ size_t len = EC_get_builtin_curves(NULL, 0);
+
+ curves = emalloc(sizeof(EC_builtin_curve) * len);
+ if (!EC_get_builtin_curves(curves, len)) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+ for (i = 0; i < len; i++) {
+ sname = OBJ_nid2sn(curves[i].nid);
+ if (sname != NULL) {
+ add_next_index_string(return_value, sname);
+ }
+ }
+ efree(curves);
+}
+#endif
+/* }}} */
+
/* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false])
Computes digest hash value for given data using given method, returns raw or binhex encoded string */
PHP_FUNCTION(openssl_digest)
@@ -5221,7 +5998,7 @@ PHP_FUNCTION(openssl_digest)
char *data, *method;
size_t data_len, method_len;
const EVP_MD *mdtype;
- EVP_MD_CTX md_ctx;
+ EVP_MD_CTX *md_ctx;
unsigned int siglen;
zend_string *sigbuf;
@@ -5237,9 +6014,10 @@ PHP_FUNCTION(openssl_digest)
siglen = EVP_MD_size(mdtype);
sigbuf = zend_string_alloc(siglen, 0);
- EVP_DigestInit(&md_ctx, mdtype);
- EVP_DigestUpdate(&md_ctx, (unsigned char *)data, data_len);
- if (EVP_DigestFinal (&md_ctx, (unsigned char *)ZSTR_VAL(sigbuf), &siglen)) {
+ md_ctx = EVP_MD_CTX_create();
+ if (EVP_DigestInit(md_ctx, mdtype) &&
+ EVP_DigestUpdate(md_ctx, (unsigned char *)data, data_len) &&
+ EVP_DigestFinal (md_ctx, (unsigned char *)ZSTR_VAL(sigbuf), &siglen)) {
if (raw_output) {
ZSTR_VAL(sigbuf)[siglen] = '\0';
ZSTR_LEN(sigbuf) = siglen;
@@ -5254,19 +6032,69 @@ PHP_FUNCTION(openssl_digest)
RETVAL_STR(digest_str);
}
} else {
+ php_openssl_store_errors();
zend_string_release(sigbuf);
RETVAL_FALSE;
}
+
+ EVP_MD_CTX_destroy(md_ctx);
+}
+/* }}} */
+
+/* Cipher mode info */
+struct php_openssl_cipher_mode {
+ zend_bool is_aead;
+ zend_bool is_single_run_aead;
+ int aead_get_tag_flag;
+ int aead_set_tag_flag;
+ int aead_ivlen_flag;
+};
+
+static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type) /* {{{ */
+{
+ switch (EVP_CIPHER_mode(cipher_type)) {
+#ifdef EVP_CIPH_GCM_MODE
+ case EVP_CIPH_GCM_MODE:
+ mode->is_aead = 1;
+ mode->is_single_run_aead = 0;
+ mode->aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG;
+ mode->aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG;
+ mode->aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN;
+ break;
+#endif
+#ifdef EVP_CIPH_CCM_MODE
+ case EVP_CIPH_CCM_MODE:
+ mode->is_aead = 1;
+ mode->is_single_run_aead = 1;
+ mode->aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG;
+ mode->aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG;
+ mode->aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN;
+ break;
+#endif
+ default:
+ memset(mode, 0, sizeof(struct php_openssl_cipher_mode));
+ }
}
/* }}} */
-static zend_bool php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_required_len)
+static int php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_required_len,
+ zend_bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */
{
char *iv_new;
/* Best case scenario, user behaved */
if (*piv_len == iv_required_len) {
- return 0;
+ return SUCCESS;
+ }
+
+ if (mode->is_aead) {
+ if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_ivlen_flag, *piv_len, NULL) != 1) {
+ php_error_docref(NULL, E_WARNING,
+ "Setting of IV length for AEAD mode failed, the expected length is %zd bytes",
+ iv_required_len);
+ return FAILURE;
+ }
+ return SUCCESS;
}
iv_new = ecalloc(1, iv_required_len + 1);
@@ -5275,82 +6103,181 @@ static zend_bool php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_
/* BC behavior */
*piv_len = iv_required_len;
*piv = iv_new;
- return 1;
+ *free_iv = 1;
+ return SUCCESS;
+
}
if (*piv_len < iv_required_len) {
- php_error_docref(NULL, E_WARNING, "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0", *piv_len, iv_required_len);
+ php_error_docref(NULL, E_WARNING,
+ "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0",
+ *piv_len, iv_required_len);
memcpy(iv_new, *piv, *piv_len);
*piv_len = iv_required_len;
*piv = iv_new;
- return 1;
+ *free_iv = 1;
+ return SUCCESS;
}
- php_error_docref(NULL, E_WARNING, "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating", *piv_len, iv_required_len);
+ php_error_docref(NULL, E_WARNING,
+ "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating",
+ *piv_len, iv_required_len);
memcpy(iv_new, *piv, iv_required_len);
*piv_len = iv_required_len;
*piv = iv_new;
- return 1;
+ *free_iv = 1;
+ return SUCCESS;
+
+}
+/* }}} */
+
+static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
+ EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
+ char **ppassword, size_t *ppassword_len, zend_bool *free_password,
+ char **piv, size_t *piv_len, zend_bool *free_iv,
+ char *tag, int tag_len, zend_long options, int enc) /* {{{ */
+{
+ unsigned char *key;
+ int key_len, password_len;
+ size_t max_iv_len;
+
+ /* check and set key */
+ password_len = (int) *ppassword_len;
+ key_len = EVP_CIPHER_key_length(cipher_type);
+ if (key_len > password_len) {
+ key = emalloc(key_len);
+ memset(key, 0, key_len);
+ memcpy(key, *ppassword, password_len);
+ *ppassword = (char *) key;
+ *ppassword_len = key_len;
+ *free_password = 1;
+ } else {
+ key = (unsigned char*)*ppassword;
+ *free_password = 0;
+ }
+ max_iv_len = EVP_CIPHER_iv_length(cipher_type);
+ if (enc && *piv_len == 0 && max_iv_len > 0 && !mode->is_aead) {
+ php_error_docref(NULL, E_WARNING,
+ "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
+ }
+
+ if (!EVP_CipherInit_ex(cipher_ctx, cipher_type, NULL, NULL, NULL, enc)) {
+ return FAILURE;
+ }
+ if (php_openssl_validate_iv(piv, piv_len, max_iv_len, free_iv, cipher_ctx, mode) == FAILURE) {
+ return FAILURE;
+ }
+ if (mode->is_single_run_aead && enc) {
+ EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL);
+ } else if (!enc && tag && tag_len > 0) {
+ if (!mode->is_aead) {
+ php_error_docref(NULL, E_WARNING, "The tag cannot be used because the cipher method does not support AEAD");
+ } else if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, (unsigned char *) tag)) {
+ php_error_docref(NULL, E_WARNING, "Setting tag for AEAD cipher decryption failed");
+ return FAILURE;
+ }
+ }
+ if (password_len > key_len) {
+ EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len);
+ }
+ if (!EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)*piv, enc)) {
+ return FAILURE;
+ }
+ if (options & OPENSSL_ZERO_PADDING) {
+ EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
+ }
+
+ return SUCCESS;
}
+/* }}} */
-/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv='']])
+static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
+ EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
+ zend_string **poutbuf, int *poutlen, char *data, size_t data_len,
+ char *aad, size_t aad_len, int enc) /* {{{ */
+{
+ int i = 0;
+
+ if (mode->is_single_run_aead && !EVP_EncryptUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
+ php_error_docref(NULL, E_WARNING, "Setting of data length failed");
+ return FAILURE;
+ }
+
+ if (mode->is_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, (unsigned char *)aad, (int)aad_len)) {
+ php_error_docref(NULL, E_WARNING, "Setting of additional application data failed");
+ return FAILURE;
+ }
+
+ *poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
+
+ if ((!enc || data_len > 0) &&
+ !EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
+ &i, (unsigned char *)data, (int)data_len)) {
+ /* we don't show warning when we fail but if we ever do, then it should look like this:
+ if (mode->is_single_run_aead && !enc) {
+ php_error_docref(NULL, E_WARNING, "Tag verifycation failed");
+ } else {
+ php_error_docref(NULL, E_WARNING, enc ? "Encryption failed" : "Decryption failed");
+ }
+ */
+ zend_string_release(*poutbuf);
+ return FAILURE;
+ }
+
+ *poutlen = i;
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv=''[, string &$tag = ''[, string $aad = ''[, long $tag_length = 16]]]]])
Encrypts given data with given method and key, returns raw or base64 encoded string */
PHP_FUNCTION(openssl_encrypt)
{
- zend_long options = 0;
- char *data, *method, *password, *iv = "";
- size_t data_len, method_len, password_len, iv_len = 0, max_iv_len;
+ zend_long options = 0, tag_len = 16;
+ char *data, *method, *password, *iv = "", *aad = "";
+ size_t data_len, method_len, password_len, iv_len = 0, aad_len = 0;
+ zval *tag = NULL;
const EVP_CIPHER *cipher_type;
- EVP_CIPHER_CTX cipher_ctx;
- int i=0, outlen, keylen;
+ EVP_CIPHER_CTX *cipher_ctx;
+ struct php_openssl_cipher_mode mode;
+ int i=0, outlen;
zend_string *outbuf;
- unsigned char *key;
- zend_bool free_iv;
+ zend_bool free_iv = 0, free_password = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsz/sl", &data, &data_len, &method, &method_len,
+ &password, &password_len, &options, &iv, &iv_len, &tag, &aad, &aad_len, &tag_len) == FAILURE) {
return;
}
+
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(aad_len, aad);
+ PHP_OPENSSL_CHECK_LONG_TO_INT(tag_len, tag_len);
+
cipher_type = EVP_get_cipherbyname(method);
if (!cipher_type) {
php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
RETURN_FALSE;
}
- PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
-
- keylen = EVP_CIPHER_key_length(cipher_type);
- if (keylen > password_len) {
- key = emalloc(keylen);
- memset(key, 0, keylen);
- memcpy(key, password, password_len);
- } else {
- key = (unsigned char*)password;
+ cipher_ctx = EVP_CIPHER_CTX_new();
+ if (!cipher_ctx) {
+ php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
+ RETURN_FALSE;
}
- max_iv_len = EVP_CIPHER_iv_length(cipher_type);
- if (iv_len == 0 && max_iv_len > 0) {
- php_error_docref(NULL, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
- }
- free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len);
+ php_openssl_load_cipher_mode(&mode, cipher_type);
- outlen = (int)data_len + EVP_CIPHER_block_size(cipher_type);
- outbuf = zend_string_alloc(outlen, 0);
- EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL);
- if (password_len > keylen) {
- PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
- EVP_CIPHER_CTX_set_key_length(&cipher_ctx, (int)password_len);
- }
- EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
- if (options & OPENSSL_ZERO_PADDING) {
- EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0);
- }
- if (data_len > 0) {
- EVP_EncryptUpdate(&cipher_ctx, (unsigned char*)ZSTR_VAL(outbuf), &i, (unsigned char *)data, (int)data_len);
- }
- outlen = i;
- if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + i, &i)) {
+ if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
+ &password, &password_len, &free_password,
+ &iv, &iv_len, &free_iv, NULL, tag_len, options, 1) == FAILURE ||
+ php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
+ data, data_len, aad, aad_len, 1) == FAILURE) {
+ RETVAL_FALSE;
+ } else if (EVP_EncryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
outlen += i;
if (options & OPENSSL_RAW_DATA) {
ZSTR_VAL(outbuf)[outlen] = '\0';
@@ -5361,38 +6288,67 @@ PHP_FUNCTION(openssl_encrypt)
base64_str = php_base64_encode((unsigned char*)ZSTR_VAL(outbuf), outlen);
zend_string_release(outbuf);
+ outbuf = base64_str;
RETVAL_STR(base64_str);
}
+ if (mode.is_aead && tag) {
+ zend_string *tag_str = zend_string_alloc(tag_len, 0);
+
+ if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode.aead_get_tag_flag, tag_len, ZSTR_VAL(tag_str)) == 1) {
+ zval_dtor(tag);
+ ZSTR_VAL(tag_str)[tag_len] = '\0';
+ ZSTR_LEN(tag_str) = tag_len;
+ ZVAL_NEW_STR(tag, tag_str);
+ } else {
+ php_error_docref(NULL, E_WARNING, "Retrieving verification tag failed");
+ zend_string_release(tag_str);
+ zend_string_release(outbuf);
+ RETVAL_FALSE;
+ }
+ } else if (tag) {
+ zval_dtor(tag);
+ ZVAL_NULL(tag);
+ php_error_docref(NULL, E_WARNING,
+ "The authenticated tag cannot be provided for cipher that doesn not support AEAD");
+ } else if (mode.is_aead) {
+ php_error_docref(NULL, E_WARNING, "A tag should be provided when using AEAD mode");
+ zend_string_release(outbuf);
+ RETVAL_FALSE;
+ }
} else {
+ php_openssl_store_errors();
zend_string_release(outbuf);
RETVAL_FALSE;
}
- if (key != (unsigned char*)password) {
- efree(key);
+
+ if (free_password) {
+ efree(password);
}
if (free_iv) {
efree(iv);
}
- EVP_CIPHER_CTX_cleanup(&cipher_ctx);
+ EVP_CIPHER_CTX_cleanup(cipher_ctx);
+ EVP_CIPHER_CTX_free(cipher_ctx);
}
/* }}} */
-/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = '']])
+/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = ''[, string $tag = ''[, string $aad = '']]]])
Takes raw or base64 encoded string and decrypts it using given method and key */
PHP_FUNCTION(openssl_decrypt)
{
zend_long options = 0;
- char *data, *method, *password, *iv = "";
- size_t data_len, method_len, password_len, iv_len = 0;
+ char *data, *method, *password, *iv = "", *tag = NULL, *aad = "";
+ size_t data_len, method_len, password_len, iv_len = 0, tag_len = 0, aad_len = 0;
const EVP_CIPHER *cipher_type;
- EVP_CIPHER_CTX cipher_ctx;
- int i, outlen, keylen;
+ EVP_CIPHER_CTX *cipher_ctx;
+ struct php_openssl_cipher_mode mode;
+ int outlen, i = 0;
zend_string *outbuf;
- unsigned char *key;
zend_string *base64_str = NULL;
- zend_bool free_iv;
+ zend_bool free_iv = 0, free_password = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsss", &data, &data_len, &method, &method_len,
+ &password, &password_len, &options, &iv, &iv_len, &tag, &tag_len, &aad, &aad_len) == FAILURE) {
return;
}
@@ -5402,6 +6358,9 @@ PHP_FUNCTION(openssl_decrypt)
}
PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(aad_len, aad);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(tag_len, tag);
cipher_type = EVP_get_cipherbyname(method);
if (!cipher_type) {
@@ -5409,52 +6368,45 @@ PHP_FUNCTION(openssl_decrypt)
RETURN_FALSE;
}
+ cipher_ctx = EVP_CIPHER_CTX_new();
+ if (!cipher_ctx) {
+ php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
+ RETURN_FALSE;
+ }
+
+ php_openssl_load_cipher_mode(&mode, cipher_type);
+
if (!(options & OPENSSL_RAW_DATA)) {
- base64_str = php_base64_decode((unsigned char*)data, (int)data_len);
+ base64_str = php_base64_decode_ex((unsigned char*)data, (int)data_len, 1);
if (!base64_str) {
php_error_docref(NULL, E_WARNING, "Failed to base64 decode the input");
+ EVP_CIPHER_CTX_free(cipher_ctx);
RETURN_FALSE;
}
data_len = ZSTR_LEN(base64_str);
data = ZSTR_VAL(base64_str);
}
- keylen = EVP_CIPHER_key_length(cipher_type);
- if (keylen > password_len) {
- key = emalloc(keylen);
- memset(key, 0, keylen);
- memcpy(key, password, password_len);
- } else {
- key = (unsigned char*)password;
- }
-
- free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type));
-
- outlen = (int)data_len + EVP_CIPHER_block_size(cipher_type);
- outbuf = zend_string_alloc(outlen, 0);
-
- EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL);
- if (password_len > keylen) {
- PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
- EVP_CIPHER_CTX_set_key_length(&cipher_ctx, (int)password_len);
- }
- EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
- if (options & OPENSSL_ZERO_PADDING) {
- EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0);
- }
- EVP_DecryptUpdate(&cipher_ctx, (unsigned char*)ZSTR_VAL(outbuf), &i, (unsigned char *)data, (int)data_len);
- outlen = i;
- if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + i, &i)) {
+ if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
+ &password, &password_len, &free_password,
+ &iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE ||
+ php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
+ data, data_len, aad, aad_len, 0) == FAILURE) {
+ RETVAL_FALSE;
+ } else if (mode.is_single_run_aead ||
+ EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
outlen += i;
ZSTR_VAL(outbuf)[outlen] = '\0';
ZSTR_LEN(outbuf) = outlen;
RETVAL_STR(outbuf);
} else {
+ php_openssl_store_errors();
zend_string_release(outbuf);
RETVAL_FALSE;
}
- if (key != (unsigned char*)password) {
- efree(key);
+
+ if (free_password) {
+ efree(password);
}
if (free_iv) {
efree(iv);
@@ -5462,7 +6414,8 @@ PHP_FUNCTION(openssl_decrypt)
if (base64_str) {
zend_string_release(base64_str);
}
- EVP_CIPHER_CTX_cleanup(&cipher_ctx);
+ EVP_CIPHER_CTX_cleanup(cipher_ctx);
+ EVP_CIPHER_CTX_free(cipher_ctx);
}
/* }}} */
@@ -5493,47 +6446,6 @@ PHP_FUNCTION(openssl_cipher_iv_length)
/* }}} */
-/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
- Computes shared secret for public value of remote DH key and local DH key */
-PHP_FUNCTION(openssl_dh_compute_key)
-{
- zval *key;
- char *pub_str;
- size_t pub_len;
- EVP_PKEY *pkey;
- BIGNUM *pub;
- zend_string *data;
- int len;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "sr", &pub_str, &pub_len, &key) == FAILURE) {
- return;
- }
- if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
- RETURN_FALSE;
- }
- if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DH || !pkey->pkey.dh) {
- RETURN_FALSE;
- }
-
- PHP_OPENSSL_CHECK_SIZE_T_TO_INT(pub_len, pub_key);
- pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
-
- data = zend_string_alloc(DH_size(pkey->pkey.dh), 0);
- len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, pkey->pkey.dh);
-
- if (len >= 0) {
- ZSTR_LEN(data) = len;
- ZSTR_VAL(data)[len] = 0;
- RETVAL_STR(data);
- } else {
- zend_string_release(data);
- RETVAL_FALSE;
- }
-
- BN_free(pub);
-}
-/* }}} */
-
/* {{{ proto string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result])
Returns a string of the length specified filled with random pseudo bytes */
PHP_FUNCTION(openssl_random_pseudo_bytes)
@@ -5576,6 +6488,8 @@ PHP_FUNCTION(openssl_random_pseudo_bytes)
ZVAL_FALSE(zstrong_result_returned);
}
RETURN_FALSE;
+ } else {
+ php_openssl_store_errors();
}
#endif