From: David Carlier Date: Mon, 27 Jun 2016 06:42:49 +0000 (+0100) Subject: couple of resource leaks fixes and use after free's X-Git-Tag: php-7.1.0alpha3~75^2~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=85e985f191c228cb6b119ebe223d682c9166fdc2;p=php couple of resource leaks fixes and use after free's --- diff --git a/NEWS b/NEWS index 18594c5ff5..ed47f0bcb0 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 2016, PHP 7.1.0beta1 +- CURL: + . Add curl_multi_errno(), curl_share_errno() and curl_share_strerror() + functions. (Pierrick) + - PCRE: . Fixed bug #72476 (Memleak in jit_stack). (Laruence) . Fixed bug #72463 (mail fails with invalid argument). (Anatol) @@ -9,6 +13,10 @@ PHP NEWS - Standard: . array_multisort now uses zend_sort instead zend_qsort. (Laruence) +- OpenSSL: + . Implemented FR #61204 (Add elliptic curve support for OpenSSL). + (Dominic Luechinger) + 23 Jun 2016, PHP 7.1.0alpha2 - Core: diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 82e99864c2..6ea744d460 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -392,6 +392,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_curl_multi_close, 0) ZEND_ARG_INFO(0, mh) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_curl_multi_errno, 0) + ZEND_ARG_INFO(0, mh) +ZEND_END_ARG_INFO() + #if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */ ZEND_BEGIN_ARG_INFO(arginfo_curl_strerror, 0) ZEND_ARG_INFO(0, errornum) @@ -400,6 +404,10 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_curl_multi_strerror, 0) ZEND_ARG_INFO(0, errornum) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_curl_share_strerror, 0) + ZEND_ARG_INFO(0, errornum) +ZEND_END_ARG_INFO() #endif ZEND_BEGIN_ARG_INFO(arginfo_curl_share_init, 0) @@ -415,6 +423,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_curl_share_setopt, 0) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_curl_share_errno, 0) + ZEND_ARG_INFO(0, sh) +ZEND_END_ARG_INFO() + #if LIBCURL_VERSION_NUM >= 0x071200 /* Available since 7.18.0 */ ZEND_BEGIN_ARG_INFO(arginfo_curl_pause, 0) ZEND_ARG_INFO(0, ch) @@ -445,6 +457,7 @@ const zend_function_entry curl_functions[] = { #if LIBCURL_VERSION_NUM >= 0x070c00 /* 7.12.0 */ PHP_FE(curl_strerror, arginfo_curl_strerror) PHP_FE(curl_multi_strerror, arginfo_curl_multi_strerror) + PHP_FE(curl_share_strerror, arginfo_curl_share_strerror) #endif #if LIBCURL_VERSION_NUM >= 0x070c01 /* 7.12.1 */ PHP_FE(curl_reset, arginfo_curl_reset) @@ -464,12 +477,14 @@ const zend_function_entry curl_functions[] = { PHP_FE(curl_multi_getcontent, arginfo_curl_multi_getcontent) PHP_FE(curl_multi_info_read, arginfo_curl_multi_info_read) PHP_FE(curl_multi_close, arginfo_curl_multi_close) + PHP_FE(curl_multi_errno, arginfo_curl_multi_errno) #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ PHP_FE(curl_multi_setopt, arginfo_curl_multi_setopt) #endif PHP_FE(curl_share_init, arginfo_curl_share_init) PHP_FE(curl_share_close, arginfo_curl_share_close) PHP_FE(curl_share_setopt, arginfo_curl_share_setopt) + PHP_FE(curl_share_errno, arginfo_curl_share_errno) PHP_FE(curl_file_create, arginfo_curlfile_create) PHP_FE_END }; diff --git a/ext/curl/multi.c b/ext/curl/multi.c index ab6d56c438..da99f8f140 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -49,6 +49,8 @@ #include #endif +#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err; + /* {{{ proto resource curl_multi_init(void) Returns a new cURL multi handle */ PHP_FUNCTION(curl_multi_init) @@ -77,6 +79,7 @@ PHP_FUNCTION(curl_multi_add_handle) php_curlm *mh; php_curl *ch; zval tmp_val; + CURLMcode error = CURLM_OK; if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &z_mh, &z_ch) == FAILURE) { return; @@ -97,7 +100,10 @@ PHP_FUNCTION(curl_multi_add_handle) zend_llist_add_element(&mh->easyh, &tmp_val); - RETURN_LONG((zend_long)curl_multi_add_handle(mh->multi, ch->cp)); + error = curl_multi_add_handle(mh->multi, ch->cp); + SAVE_CURLM_ERROR(mh, error); + + RETURN_LONG((zend_long) error); } /* }}} */ @@ -137,6 +143,7 @@ PHP_FUNCTION(curl_multi_remove_handle) zval *z_ch; php_curlm *mh; php_curl *ch; + CURLMcode error = CURLM_OK; if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &z_mh, &z_ch) == FAILURE) { return; @@ -150,7 +157,10 @@ PHP_FUNCTION(curl_multi_remove_handle) RETURN_FALSE; } - RETVAL_LONG((zend_long)curl_multi_remove_handle(mh->multi, ch->cp)); + error = curl_multi_remove_handle(mh->multi, ch->cp); + SAVE_CURLM_ERROR(mh, error); + + RETVAL_LONG((zend_long) error); zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *))curl_compare_resources); } @@ -178,6 +188,7 @@ PHP_FUNCTION(curl_multi_select) int maxfd; double timeout = 1.0; struct timeval to; + CURLMcode error = CURLM_OK; if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|d", &z_mh, &timeout) == FAILURE) { return; @@ -193,7 +204,9 @@ PHP_FUNCTION(curl_multi_select) FD_ZERO(&writefds); FD_ZERO(&exceptfds); - curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd); + error = curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd); + SAVE_CURLM_ERROR(mh, error); + if (maxfd == -1) { RETURN_LONG(-1); } @@ -209,7 +222,7 @@ PHP_FUNCTION(curl_multi_exec) zval *z_still_running; php_curlm *mh; int still_running; - int result; + CURLMcode error = CURLM_OK; if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz/", &z_mh, &z_still_running) == FAILURE) { return; @@ -237,10 +250,11 @@ PHP_FUNCTION(curl_multi_exec) convert_to_long(z_still_running); still_running = Z_LVAL_P(z_still_running); - result = curl_multi_perform(mh->multi, &still_running); + error = curl_multi_perform(mh->multi, &still_running); ZVAL_LONG(z_still_running, still_running); - RETURN_LONG(result); + SAVE_CURLM_ERROR(mh, error); + RETURN_LONG((zend_long) error); } /* }}} */ @@ -383,6 +397,25 @@ void _php_curl_multi_close(zend_resource *rsrc) /* {{{ */ } /* }}} */ +/* {{{ proto int curl_multi_errno(resource mh) + Return an integer containing the last multi curl error number */ +PHP_FUNCTION(curl_multi_errno) +{ + zval *z_mh; + php_curlm *mh; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_mh) == FAILURE) { + return; + } + + if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) { + RETURN_FALSE; + } + + RETURN_LONG(mh->err.no); +} +/* }}} */ + #if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */ /* {{{ proto bool curl_multi_strerror(int code) return string describing error code */ @@ -433,6 +466,7 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, break; } + SAVE_CURLM_ERROR(mh, error); if (error != CURLM_OK) { return 1; } else { diff --git a/ext/curl/php_curl.h b/ext/curl/php_curl.h index d3a22c7c30..02077aaf27 100644 --- a/ext/curl/php_curl.h +++ b/ext/curl/php_curl.h @@ -90,14 +90,17 @@ PHP_FUNCTION(curl_multi_info_read); PHP_FUNCTION(curl_multi_init); PHP_FUNCTION(curl_multi_remove_handle); PHP_FUNCTION(curl_multi_select); +PHP_FUNCTION(curl_multi_errno); PHP_FUNCTION(curl_share_close); PHP_FUNCTION(curl_share_init); PHP_FUNCTION(curl_share_setopt); +PHP_FUNCTION(curl_share_errno); #if LIBCURL_VERSION_NUM >= 0x070c00 /* 7.12.0 */ PHP_FUNCTION(curl_strerror); PHP_FUNCTION(curl_multi_strerror); +PHP_FUNCTION(curl_share_strerror); #endif #if LIBCURL_VERSION_NUM >= 0x070c01 /* 7.12.1 */ @@ -114,6 +117,7 @@ PHP_FUNCTION(curl_multi_setopt); #if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */ PHP_FUNCTION(curl_pause); #endif + PHP_FUNCTION(curl_file_create); @@ -190,10 +194,16 @@ typedef struct { int still_running; CURLM *multi; zend_llist easyh; + struct { + int no; + } err; } php_curlm; typedef struct { CURLSH *share; + struct { + int no; + } err; } php_curlsh; void _php_curl_cleanup_handle(php_curl *); diff --git a/ext/curl/share.c b/ext/curl/share.c index 3806e2778b..983cc2750e 100644 --- a/ext/curl/share.c +++ b/ext/curl/share.c @@ -32,6 +32,8 @@ #include +#define SAVE_CURLSH_ERROR(__handle, __err) (__handle)->err.no = (int) __err; + /* {{{ proto void curl_share_init() Initialize a share curl handle */ PHP_FUNCTION(curl_share_init) @@ -85,6 +87,7 @@ static int _php_curl_share_setopt(php_curlsh *sh, zend_long option, zval *zvalue break; } + SAVE_CURLSH_ERROR(sh, error); if (error != CURLSHE_OK) { return 1; } else { @@ -128,6 +131,48 @@ void _php_curl_share_close(zend_resource *rsrc) /* {{{ */ } /* }}} */ +/* {{{ proto int curl_share_errno(resource mh) + Return an integer containing the last share curl error number */ +PHP_FUNCTION(curl_share_errno) +{ + zval *z_sh; + php_curlsh *sh; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_sh) == FAILURE) { + return; + } + + if ((sh = (php_curlsh *)zend_fetch_resource(Z_RES_P(z_sh), le_curl_share_handle_name, le_curl_share_handle)) == NULL) { + RETURN_FALSE; + } + + RETURN_LONG(sh->err.no); +} +/* }}} */ + + +#if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */ +/* {{{ proto bool curl_share_strerror(int code) + return string describing error code */ +PHP_FUNCTION(curl_share_strerror) +{ + zend_long code; + const char *str; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &code) == FAILURE) { + return; + } + + str = curl_share_strerror(code); + if (str) { + RETURN_STRING(str); + } else { + RETURN_NULL(); + } +} +/* }}} */ +#endif + #endif /* diff --git a/ext/curl/tests/curl_multi_errno_strerror_001.phpt b/ext/curl/tests/curl_multi_errno_strerror_001.phpt new file mode 100644 index 0000000000..1fcdfe9558 --- /dev/null +++ b/ext/curl/tests/curl_multi_errno_strerror_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +curl_multi_errno and curl_multi_strerror basic test +--SKIPIF-- += 7.15.4"); +} +?> +--FILE-- + +--EXPECTF-- +0 +No error +6 +Unknown option diff --git a/ext/curl/tests/curl_share_errno_strerror_001.phpt b/ext/curl/tests/curl_share_errno_strerror_001.phpt new file mode 100644 index 0000000000..91476cdaff --- /dev/null +++ b/ext/curl/tests/curl_share_errno_strerror_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +curl_share_errno and curl_share_strerror basic test +--SKIPIF-- += 7.12.0"); +} +?> +--FILE-- + +--EXPECTF-- +0 +No error +1 +Unknown share option diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 6ac4f0e35f..6d34851352 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -791,6 +791,7 @@ int zend_file_cache_script_store(zend_persistent_script *script, int in_shm) if (writev(fd, vec, 3) != (ssize_t)(sizeof(info) + script->size + info.str_size)) { zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename); zend_string_release((zend_string*)ZCG(mem)); + close(fd); efree(mem); unlink(filename); efree(filename); @@ -804,6 +805,7 @@ int zend_file_cache_script_store(zend_persistent_script *script, int in_shm) ) { zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename); zend_string_release((zend_string*)ZCG(mem)); + close(fd); efree(mem); unlink(filename); efree(filename); diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 32c27c326f..95e5b54f1e 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -118,6 +118,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); @@ -379,6 +382,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) @@ -515,6 +523,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) @@ -672,6 +683,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; @@ -761,7 +776,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; @@ -1035,6 +1050,18 @@ static int php_openssl_parse_config(struct php_x509_request * req, zval * option } 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"); @@ -3730,6 +3757,26 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req } } 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; #endif default: php_error_docref(NULL, E_WARNING, "Unsupported private key type"); @@ -3805,24 +3852,30 @@ 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; \ +#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) do { \ + if (pkey->pkey._type->_name != NULL) { \ + OPENSSL_GET_BN(_type, pkey->pkey._type->_name, _name); \ + } \ + } 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); \ - } \ + Z_TYPE_P(bn) == IS_STRING) { \ + _type->_name = BN_bin2bn( \ + (unsigned char*)Z_STRVAL_P(bn), \ + (int)Z_STRLEN_P(bn), NULL); \ + } \ } while (0); /* {{{ php_openssl_pkey_init_dsa */ @@ -3968,6 +4021,118 @@ PHP_FUNCTION(openssl_pkey_new) 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 } } @@ -4303,13 +4468,18 @@ PHP_FUNCTION(openssl_pkey_get_details) if (pkey->pkey.ec != 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); @@ -4327,11 +4497,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(pkey->pkey.ec)) != NULL) { + OPENSSL_GET_BN(ec, d, d); + } + add_assoc_zval(return_value, "ec", &ec); + + BN_free(x); + BN_free(y); } break; #endif @@ -5540,6 +5726,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; + int 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) diff --git a/ext/openssl/tests/029.phpt b/ext/openssl/tests/029.phpt new file mode 100644 index 0000000000..cf43e0e538 --- /dev/null +++ b/ext/openssl/tests/029.phpt @@ -0,0 +1,254 @@ +--TEST-- +openssl_pkey_new() with EC key +--SKIPIF-- + +--FILE-- + no private key informations\n"; +$detailsCopy3 = $details; +unset($detailsCopy3["ec"]["d"]); +$key3 = openssl_pkey_new($detailsCopy3); +$details3 = openssl_pkey_get_details($key3); +print_r($details3); +var_dump(array_diff($details["ec"], $details3["ec"])); +$privateKey3 = openssl_pkey_get_private($key3); +var_dump($privateKey3); +$publicKey3 = openssl_pkey_get_public($key3); +var_dump($publicKey3); +var_dump($details["key"] === $details3["key"]); + +echo "Missing 'x' parameter will not change the details. The public key is calculated from the private key 'd'\n"; +$detailsCopy4 = $details; +unset($detailsCopy4["ec"]["x"]); +$key4 = openssl_pkey_new($detailsCopy4); +$details4 = openssl_pkey_get_details($key4); +print_r($details4); +var_dump(array_diff($details["ec"], $details4["ec"])); + +echo "Missing 'y' parameter will not change the details. The public key is calculated from the private key 'd'\n"; +$detailsCopy5 = $details; +unset($detailsCopy5["ec"]["y"]); +$key5 = openssl_pkey_new($detailsCopy5); +$details5 = openssl_pkey_get_details($key5); +print_r($details5); +var_dump(array_diff($details["ec"], $details5["ec"])); + +echo "Missing 'd' and 'x' parameters will generate a new public key pair\n"; +$detailsCopy6 = $details; +unset($detailsCopy6["ec"]["d"]); +unset($detailsCopy6["ec"]["x"]); +$key6 = openssl_pkey_new($detailsCopy6); +$details6 = openssl_pkey_get_details($key6); +print_r($details6); +var_dump(array_diff($details["ec"], $details6["ec"])); + +echo "Missing 'd' and 'y' parameters will generate a new public key pair\n"; +$detailsCopy7 = $details; +unset($detailsCopy7["ec"]["d"]); +unset($detailsCopy7["ec"]["y"]); +$key7 = openssl_pkey_new($detailsCopy7); +$details7 = openssl_pkey_get_details($key7); +print_r($details7); +var_dump(array_diff($details["ec"], $details7["ec"])); + +// Tests vectors from http://point-at-infinity.org/ecc/nisttv +echo "Create a private key from scratch 1\n"; +$detailsFromScratch8 = array( + "ec" => array( + "curve_name" => "prime256v1", + "d" => "\1", + ), +); +$key8 = openssl_pkey_new($detailsFromScratch8); +$details8 = openssl_pkey_get_details($key8); +var_dump(strtoupper(bin2hex($details8["ec"]["x"])) === "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); +var_dump(strtoupper(bin2hex($details8["ec"]["y"])) === "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); + +echo "Create a private key from scratch 2\n"; +$detailsFromScratch9 = array( + "ec" => array( + "curve_name" => "prime192v1", + "d" => hex2bin("7FFFFFFFFE0000007FFFFE003FFFFFE0007FFF1FFFFE0800"), + ), +); +$key9 = openssl_pkey_new($detailsFromScratch9); +$details9 = openssl_pkey_get_details($key9); +var_dump(strtoupper(bin2hex($details9["ec"]["x"])) === "45DAF0A306121BDB3B82E734CB44FDF65C9930F0E4FD2068"); +var_dump(strtoupper(bin2hex($details9["ec"]["y"])) === "F039FACE58EB7DE34E3374ADB28DF81F019C4548BAA75B64"); +?> +--EXPECTF-- +Array +( + [bits] => 256 + [key] => -----BEGIN PUBLIC KEY-----%a +-----END PUBLIC KEY----- + + [ec] => Array + ( + [curve_name] => prime256v1 + [curve_oid] => 1.2.840.10045.3.1.7 + [x] => %a + [y] => %a + [d] => %a + ) + + [type] => 3 +) +Use details an create the same key pair +Array +( + [bits] => 256 + [key] => -----BEGIN PUBLIC KEY-----%a +-----END PUBLIC KEY----- + + [ec] => Array + ( + [curve_name] => prime256v1 + [curve_oid] => 1.2.840.10045.3.1.7 + [x] => %a + [y] => %a + [d] => %a + ) + + [type] => 3 +) +array(0) { +} +Missing 'd' parameter (private key part) => no private key informations +Array +( + [bits] => 256 + [key] => -----BEGIN PUBLIC KEY-----%a +-----END PUBLIC KEY----- + + [ec] => Array + ( + [curve_name] => prime256v1 + [curve_oid] => 1.2.840.10045.3.1.7 + [x] => %a + [y] => %a + ) + + [type] => 3 +) +array(1) { + ["d"]=> + string(32) "%a" +} + +Warning: openssl_pkey_get_private(): supplied key param is a public key in %s on line %d +bool(false) +resource(%d) of type (OpenSSL key) +bool(true) +Missing 'x' parameter will not change the details. The public key is calculated from the private key 'd' +Array +( + [bits] => 256 + [key] => -----BEGIN PUBLIC KEY-----%a +-----END PUBLIC KEY----- + + [ec] => Array + ( + [curve_name] => prime256v1 + [curve_oid] => 1.2.840.10045.3.1.7 + [x] => %a + [y] => %a + [d] => %a + ) + + [type] => 3 +) +array(0) { +} +Missing 'y' parameter will not change the details. The public key is calculated from the private key 'd' +Array +( + [bits] => 256 + [key] => -----BEGIN PUBLIC KEY-----%a +-----END PUBLIC KEY----- + + [ec] => Array + ( + [curve_name] => prime256v1 + [curve_oid] => 1.2.840.10045.3.1.7 + [x] => %a + [y] => %a + [d] => %a + ) + + [type] => 3 +) +array(0) { +} +Missing 'd' and 'x' parameters will generate a new public key pair +Array +( + [bits] => 256 + [key] => -----BEGIN PUBLIC KEY-----%a +-----END PUBLIC KEY----- + + [ec] => Array + ( + [curve_name] => prime256v1 + [curve_oid] => 1.2.840.10045.3.1.7 + [x] => %a + [y] => %a + [d] => %a + ) + + [type] => 3 +) +array(3) { + ["x"]=> + string(32) "%a" + ["y"]=> + string(32) "%a" + ["d"]=> + string(32) "%a" +} +Missing 'd' and 'y' parameters will generate a new public key pair +Array +( + [bits] => 256 + [key] => -----BEGIN PUBLIC KEY-----%a +-----END PUBLIC KEY----- + + [ec] => Array + ( + [curve_name] => prime256v1 + [curve_oid] => 1.2.840.10045.3.1.7 + [x] => %a + [y] => %a + [d] => %a + ) + + [type] => 3 +) +array(3) { + ["x"]=> + string(32) "%a" + ["y"]=> + string(32) "%a" + ["d"]=> + string(32) "%a" +} +Create a private key from scratch 1 +bool(true) +bool(true) +Create a private key from scratch 2 +bool(true) +bool(true) diff --git a/ext/openssl/tests/ecc.phpt b/ext/openssl/tests/ecc.phpt new file mode 100644 index 0000000000..5df01304f0 --- /dev/null +++ b/ext/openssl/tests/ecc.phpt @@ -0,0 +1,110 @@ +--TEST-- +openssl_*() with OPENSSL_KEYTYPE_EC +--SKIPIF-- + +--FILE-- + "secp384r1", + "private_key_type" => OPENSSL_KEYTYPE_EC, +); +echo "Testing openssl_pkey_new\n"; +$key1 = openssl_pkey_new($args); +var_dump($key1); + +$argsFailed = array( + "curve_name" => "invalid_cuve_name", + "private_key_type" => OPENSSL_KEYTYPE_EC, +); + +$keyFailed = openssl_pkey_new($argsFailed); +var_dump($keyFailed); + +$d1 = openssl_pkey_get_details($key1); +var_dump($d1["bits"]); +var_dump(strlen($d1["key"])); +var_dump($d1["ec"]["curve_name"]); +var_dump($d1["type"] == OPENSSL_KEYTYPE_EC); + +$key2 = openssl_pkey_new($d1); +var_dump($key2); + +$d2 = openssl_pkey_get_details($key2); +// Compare array +var_dump($d1 === $d2); + +$dn = array( + "countryName" => "BR", + "stateOrProvinceName" => "Rio Grande do Sul", + "localityName" => "Porto Alegre", + "commonName" => "Henrique do N. Angelo", + "emailAddress" => "hnangelo@php.net" +); + +// openssl_csr_new creates a new public key pair if the key argument is null +echo "Testing openssl_csr_new with key generation\n"; +$keyGenerate = null; +var_dump($keyGenerate); +$csr = openssl_csr_new($dn, $keyGenerate, $args); + +var_dump($keyGenerate); + +$args["digest_alg"] = "sha1"; +echo "Testing openssl_csr_new with existing ecc key\n"; +$csr = openssl_csr_new($dn, $key1, $args); +var_dump($csr); + +$pubkey1 = openssl_pkey_get_details(openssl_csr_get_public_key($csr)); +var_dump(isset($pubkey1["ec"]["priv_key"])); +unset($d1["ec"]["priv_key"]); +var_dump(array_diff($d1["ec"], $pubkey1["ec"])); + +$x509 = openssl_csr_sign($csr, null, $key1, 365, $args); +var_dump($x509); + +echo "Testing openssl_x509_check_private_key\n"; +var_dump(openssl_x509_check_private_key($x509, $key1)); + +$key3 = openssl_pkey_new($args); +var_dump(openssl_x509_check_private_key($x509, $key3)); + +echo "Testing openssl_get_curve_names\n"; +$curve_names = openssl_get_curve_names(); + +var_dump(is_array($curve_names)); + +foreach ($curve_names as $curve_name) { + if ("secp384r1" === $curve_name) { + echo "Found secp384r1 in curve names\n"; + } +} +?> +--EXPECTF-- +Testing openssl_pkey_new +resource(%d) of type (OpenSSL key) + +Warning: openssl_pkey_new(): Unknown elliptic curve (short) name invalid_cuve_name in %s on line %d +bool(false) +int(384) +int(215) +string(9) "secp384r1" +bool(true) +resource(%d) of type (OpenSSL key) +bool(true) +Testing openssl_csr_new with key generation +NULL +resource(%d) of type (OpenSSL key) +Testing openssl_csr_new with existing ecc key +resource(%d) of type (OpenSSL X.509 CSR) +bool(false) +array(1) { + ["d"]=> + string(48) "%a" +} +resource(%d) of type (OpenSSL X.509) +Testing openssl_x509_check_private_key +bool(true) +bool(false) +Testing openssl_get_curve_names +bool(true) +Found secp384r1 in curve names diff --git a/ext/openssl/tests/openssl_pkey_export_basic.phpt b/ext/openssl/tests/openssl_pkey_export_basic.phpt index d229d6b135..530158d7d9 100644 --- a/ext/openssl/tests/openssl_pkey_export_basic.phpt +++ b/ext/openssl/tests/openssl_pkey_export_basic.phpt @@ -2,8 +2,10 @@ openssl_pkey_export() with EC key --SKIPIF-- --FILE-- --EXPECTF-- resource(%d) of type (OpenSSL key) @@ -49,6 +50,9 @@ bool(true) bool(true) bool(true) resource(%d) of type (OpenSSL key) -bool(true) +array(1) { + ["d"]=> + string(32) "%a" +} bool(true) bool(true) diff --git a/ext/openssl/tests/openssl_pkey_get_details_basic.phpt b/ext/openssl/tests/openssl_pkey_get_details_basic.phpt index 8e0cef46c0..3c239af2a2 100644 --- a/ext/openssl/tests/openssl_pkey_get_details_basic.phpt +++ b/ext/openssl/tests/openssl_pkey_get_details_basic.phpt @@ -3,11 +3,11 @@ openssl_pkey_get_details() with EC key --SKIPIF-- --FILE-- @@ -22,6 +22,9 @@ Array ( [curve_name] => prime256v1 [curve_oid] => 1.2.840.10045.3.1.7 + [x] => %a + [y] => %a + [d] => %a ) [type] => 3 diff --git a/main/fastcgi.c b/main/fastcgi.c index 3f2efc86f1..d77e8a0ae3 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -758,6 +758,7 @@ int fcgi_listen(const char *path, int backlog) bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 || listen(listen_socket, backlog) < 0) { + close(listen_socket); fcgi_log(FCGI_ERROR, "Cannot bind/listen socket - [%d] %s.\n",errno, strerror(errno)); return -1; } diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c index 2d95e494aa..8f5ce24800 100644 --- a/sapi/cli/ps_title.c +++ b/sapi/cli/ps_title.c @@ -224,8 +224,10 @@ char** save_ps_args(int argc, char** argv) for (i = 0; i < argc; i++) { new_argv[i] = strdup(argv[i]); - if (!new_argv[i]) + if (!new_argv[i]) { + free(new_argv); goto clobber_error; + } } new_argv[argc] = NULL; diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 0a45882251..ecd3f3974e 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -1433,6 +1433,7 @@ phpdbg_main: case 'i': { /* set init file */ if (init_file) { free(init_file); + init_file = NULL; } init_file_len = strlen(php_optarg); @@ -1786,7 +1787,9 @@ phpdbg_main: /* initialize from file */ PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; zend_try { - phpdbg_init(init_file, init_file_len, init_file_default); + if (init_file) { + phpdbg_init(init_file, init_file_len, init_file_default); + } if (bp_tmp) { PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; phpdbg_string_init(bp_tmp);