Skip to content

Commit

Permalink
Fixed bug #81085: Add version 7.71.0 blob options.
Browse files Browse the repository at this point in the history
Adds support for the following options beginning with version 7.71.0:

    CURLOPT_ISSUERCERT_BLOB
    CURLOPT_PROXY_ISSUERCERT
    CURLOPT_PROXY_ISSUERCERT_BLOB
    CURLOPT_PROXY_SSLCERT_BLOB
    CURLOPT_PROXY_SSLKEY_BLOB
    CURLOPT_SSLCERT_BLOB
    CURLOPT_SSLKEY_BLOB

Closes GH-7194.
  • Loading branch information
camporter authored and nikic committed Jun 28, 2021
1 parent aad0d26 commit b11785c
Show file tree
Hide file tree
Showing 8 changed files with 453 additions and 0 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ PHP NEWS
- Core:
. Fixed bug #81202 (powerpc64 build fails on fibers). (krakjoe)

- Curl:
. Fixed bug #81085 (Support CURLOPT_SSLCERT_BLOB for cert strings).
(camporter)

- Reflection:
. Fixed bug #81200 (no way to determine if Closure is static). (krakjoe)

Expand Down
10 changes: 10 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,16 @@ PHP 8.1 UPGRADE NOTES

- Curl:
. Added CURLOPT_DOH_URL option.
. Added certificate blob options when for libcurl >= 7.71.0:

CURLOPT_ISSUERCERT_BLOB
CURLOPT_PROXY_ISSUERCERT
CURLOPT_PROXY_ISSUERCERT_BLOB
CURLOPT_PROXY_SSLCERT_BLOB
CURLOPT_PROXY_SSLKEY_BLOB
CURLOPT_SSLCERT_BLOB
CURLOPT_SSLKEY_BLOB

. Added CURLStringFile, which can be used to post a file from a string rather
than a file:

Expand Down
36 changes: 36 additions & 0 deletions ext/curl/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,16 @@ PHP_MINIT_FUNCTION(curl)
REGISTER_CURL_CONSTANT(CURL_VERSION_ALTSVC);
#endif

#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
REGISTER_CURL_CONSTANT(CURLOPT_ISSUERCERT_BLOB);
REGISTER_CURL_CONSTANT(CURLOPT_PROXY_ISSUERCERT);
REGISTER_CURL_CONSTANT(CURLOPT_PROXY_ISSUERCERT_BLOB);
REGISTER_CURL_CONSTANT(CURLOPT_PROXY_SSLCERT_BLOB);
REGISTER_CURL_CONSTANT(CURLOPT_PROXY_SSLKEY_BLOB);
REGISTER_CURL_CONSTANT(CURLOPT_SSLCERT_BLOB);
REGISTER_CURL_CONSTANT(CURLOPT_SSLKEY_BLOB);
#endif

REGISTER_CURL_CONSTANT(CURLOPT_SAFE_UPLOAD);

#ifdef PHP_CURL_NEED_OPENSSL_TSL
Expand Down Expand Up @@ -2520,6 +2530,9 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i
#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */
case CURLOPT_PROXY_TLS13_CIPHERS:
case CURLOPT_TLS13_CIPHERS:
#endif
#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
case CURLOPT_PROXY_ISSUERCERT:
#endif
{
zend_string *tmp_str;
Expand Down Expand Up @@ -2923,6 +2936,29 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i
ZVAL_COPY(&ch->handlers.fnmatch->func_name, zvalue);
break;

/* Curl blob options */
#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
case CURLOPT_ISSUERCERT_BLOB:
case CURLOPT_PROXY_ISSUERCERT_BLOB:
case CURLOPT_PROXY_SSLCERT_BLOB:
case CURLOPT_PROXY_SSLKEY_BLOB:
case CURLOPT_SSLCERT_BLOB:
case CURLOPT_SSLKEY_BLOB:
{
zend_string *tmp_str;
zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);

struct curl_blob stblob;
stblob.data = ZSTR_VAL(str);
stblob.len = ZSTR_LEN(str);
stblob.flags = CURL_BLOB_COPY;
error = curl_easy_setopt(ch->cp, option, &stblob);

zend_tmp_string_release(tmp_str);
}
break;
#endif

default:
if (is_array_config) {
zend_argument_value_error(2, "must contain only valid cURL options");
Expand Down
239 changes: 239 additions & 0 deletions ext/curl/tests/curl_setopt_ssl.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
--TEST--
CURLOPT_SSL* basic client auth tests
--EXTENSIONS--
curl
--SKIPIF--
<?php
if (!function_exists("proc_open")) die("skip no proc_open");
exec('openssl version', $out, $code);
if ($code > 0) die("skip couldn't locate openssl binary");
if (PHP_OS_FAMILY === 'Windows') die('skip not for Windows');
$curl_version = curl_version();
if ($curl_version['version_number'] < 0x074700) {
die("skip: blob options not supported for curl < 7.71.0");
}
?>
--FILE--
<?php

function check_error(CurlHandle $ch) {
if (curl_errno($ch) !== 0) {
echo "CURL ERROR: " . curl_errno($ch) . "\n";
}
}

function check_response($response, $clientCertSubject) {
if (strpos($response, $clientCertSubject) === false) {
echo "client cert subject not in response\n";
} else {
echo "client cert subject in response\n";
}
}

$clientCertSubject = "Subject: C=US, ST=TX, L=Clientlocation, O=Clientcompany, CN=clientname/emailAddress=test@example.com";

// load server cert
$serverCertPath = __DIR__ . DIRECTORY_SEPARATOR . 'curl_setopt_ssl_servercert.pem';
$serverCert = file_get_contents($serverCertPath);

// load server key
$serverKeyPath = __DIR__ . DIRECTORY_SEPARATOR . 'curl_setopt_ssl_serverkey.pem';
$serverKey = file_get_contents($serverKeyPath);

// load client cert
$clientCertPath = __DIR__ . DIRECTORY_SEPARATOR . 'curl_setopt_ssl_clientcert.pem';
$clientCert = file_get_contents($clientCertPath);

// load client key
$clientKeyPath = __DIR__ . DIRECTORY_SEPARATOR . 'curl_setopt_ssl_clientkey.pem';
$clientKey = file_get_contents($clientKeyPath);

if ($serverCert === false
|| $serverKey === false
|| $clientCert === false
|| $clientKey === false
) {
die('failed to load test certs and keys for files');
}

$port = 14430;

// set up local server
$cmd = "openssl s_server -key $serverKeyPath -cert $serverCertPath -accept $port -www -CAfile $clientCertPath -verify_return_error -Verify 1";
$process = proc_open($cmd, [["pipe", "r"], ["pipe", "w"], ["pipe", "w"]], $pipes);

if ($process === false) {
die('failed to start server');
}
try {
// Give the server time to start
sleep(1);

echo "case 1: client cert and key from string\n";
$ch = curl_init("https://127.0.0.1:$port/");
var_dump(curl_setopt($ch, CURLOPT_SSLCERT_BLOB, $clientCert));
var_dump(curl_setopt($ch, CURLOPT_SSLKEY_BLOB, $clientKey));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($ch);
check_response($response, $clientCertSubject);
check_error($ch);
curl_close($ch);

echo "\n";
echo "case 2: empty client cert and key from string\n";
$ch = curl_init("https://127.0.0.1:$port/");
var_dump(curl_setopt($ch, CURLOPT_SSLCERT_BLOB, ''));
var_dump(curl_setopt($ch, CURLOPT_SSLKEY_BLOB, $clientKey));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($ch);
check_response($response, $clientCertSubject);
check_error($ch);
curl_close($ch);

echo "\n";
echo "case 3: client cert and empty key from string\n";
$ch = curl_init("https://127.0.0.1:$port/");
var_dump(curl_setopt($ch, CURLOPT_SSLCERT_BLOB, $clientCert));
var_dump(curl_setopt($ch, CURLOPT_SSLKEY_BLOB, ''));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($ch);
check_response($response, $clientCertSubject);
check_error($ch);
curl_close($ch);

echo "\n";
echo "case 4: client cert and key from file\n";
$ch = curl_init("https://127.0.0.1:$port/");
var_dump(curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath));
var_dump(curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($ch);
check_response($response, $clientCertSubject);
check_error($ch);
curl_close($ch);

echo "\n";
echo "case 5: issuer cert from file\n";
$ch = curl_init("https://127.0.0.1:$port/");
var_dump(curl_setopt($ch, CURLOPT_CAINFO, $serverCertPath));
var_dump(curl_setopt($ch, CURLOPT_ISSUERCERT, $serverCertPath));
var_dump(curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath));
var_dump(curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($ch);
check_response($response, $clientCertSubject);
check_error($ch);
curl_close($ch);

echo "\n";
echo "case 6: issuer cert from string\n";
$ch = curl_init("https://127.0.0.1:$port/");
var_dump(curl_setopt($ch, CURLOPT_CAINFO, $serverCertPath));
var_dump(curl_setopt($ch, CURLOPT_ISSUERCERT_BLOB, $serverCert));
var_dump(curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath));
var_dump(curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($ch);
check_response($response, $clientCertSubject);
check_error($ch);
curl_close($ch);

echo "\n";
echo "case 7: empty issuer cert from string\n";
$ch = curl_init("https://127.0.0.1:$port/");
var_dump(curl_setopt($ch, CURLOPT_CAINFO, $serverCertPath));
var_dump(curl_setopt($ch, CURLOPT_ISSUERCERT_BLOB, ''));
var_dump(curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath));
var_dump(curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true));
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($ch);
check_response($response, $clientCertSubject);
check_error($ch);
curl_close($ch);

} finally {
// clean up server process
proc_terminate($process);
proc_close($process);
}

?>
--EXPECT--
case 1: client cert and key from string
bool(true)
bool(true)
bool(true)
bool(true)
client cert subject in response

case 2: empty client cert and key from string
bool(true)
bool(true)
bool(true)
bool(true)
client cert subject not in response
CURL ERROR: 58

case 3: client cert and empty key from string
bool(true)
bool(true)
bool(true)
bool(true)
client cert subject not in response
CURL ERROR: 58

case 4: client cert and key from file
bool(true)
bool(true)
bool(true)
bool(true)
client cert subject in response

case 5: issuer cert from file
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
client cert subject in response

case 6: issuer cert from string
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
client cert subject in response

case 7: empty issuer cert from string
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
client cert subject not in response
CURL ERROR: 83
32 changes: 32 additions & 0 deletions ext/curl/tests/curl_setopt_ssl_clientcert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgDCCA2gCCQCpI/KBeVlPsjANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMC
VVMxCzAJBgNVBAgMAlRYMRcwFQYDVQQHDA5DbGllbnRsb2NhdGlvbjEWMBQGA1UE
CgwNQ2xpZW50Y29tcGFueTETMBEGA1UEAwwKY2xpZW50bmFtZTEfMB0GCSqGSIb3
DQEJARYQdGVzdEBleGFtcGxlLmNvbTAeFw0yMTA2MjYxOTAyMTlaFw00ODExMTAx
OTAyMTlaMIGBMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVFgxFzAVBgNVBAcMDkNs
aWVudGxvY2F0aW9uMRYwFAYDVQQKDA1DbGllbnRjb21wYW55MRMwEQYDVQQDDApj
bGllbnRuYW1lMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2TlNc7TMAjjjgERln914ScxiMcwH
5TVOjUxhBX9XuSNP8tnO03r4w6bQFCaq7ire63ssCx1aua4tCIMeKG++ueiMeqLc
fmc5lIs3GuMEYrcopf9FQvvDuRM8EpkIKNNp5e82TBQkEAtSFc1e8rPnjAxi68gm
g/yTrm74tG4sqyHtzpiNUwwP4sE3yfV6sENF+Sth8grfsTBShXmq1eQ+PWYBHT13
e4J0YBkOXduNIFuXs/mKUgFlEhzhthdznsN2dLzWzVmZZGX1nTsZ+yQYDdGVq0ay
F20unovhZNh2J9kfdgKDZFPgtRHgFYZZTeacZCGWg9KMkwVXqdufjk4M/khkpwTh
agZHhf1lLq3pmGzm8hZto5OKWXOk+hoopSEU0+V+PJtl5Cl7MIa6gJVsk6rT3abp
X5e3bmlABreZIlFjk2kO68gNbbSdZiZfjqtqLeTY2KwYsUZ358zAJlDT8Zc0T00u
pQEBQxFBNWQqfXwlnWtvcKpvwonbBZLcUGoWMiuFIgsnsTvxLWayX20Njy2m7K7d
002cn3KBVpu3EXO9NkYKg+q6z79FvAlf9Xq/nV9ubvGQ/wEpJOr4egvWHoC0qt3/
uW+nzhmqhBv6cGWnKDUVWknAsothJlunP55Dtq2fmxmXH4TjYqofhfvMN88XdxFb
0ZUYojirWTOSIn8CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAa+rI3YH3HT+KiyBE
8Mv7/gH1jlQ2zPzTu0EccgnDcuXQSlAtvz+0evoUYOpREQ/lfeNeICllJgK0R0Go
QReqsnC6Tbq/pCDMmo6g6vm7H0j1b1RNNnhNuh0el/O/cnAT6KBcrmojBBkJMHRT
vHJeE0W+rr3Qvg1GM3KfHlyxQeZLQrTeuNjE5f+9LUy1d11UV2xLuiGax5zYQQM3
iwqHFvfe3EYzmRECmMY4qLZgN/c1Cd4RK8wEPKKsvOjxpNLAAR8+VeLUj32UmtWd
ZFnTFEiJI0RWtRdLt1J3r9+ybg3c0e/BJWxK6Dw00NV6A5UNd5lVbv5v9ZMGI7tu
fTaZE4AR50yd+VMZXMMZj282eZOIIoi1IeT1uofyDcYvxWjNn+gW/Di8niKunwYA
tdXANcGtzlB3slBSX0gZ9sItdFkb7gDG86L7Gg90GJEG0ZtU8WzQ5BUArMfYWZbv
l8A0kOQi6dR+/RjcswRDXWSYx3U0j0db5Ibg7D3WMRcHQasck2YHZrlku5ILC5Gd
F0N4bIps2V6kN/OWFdSyWTwvIS2m62ovg3o9hR2aODqkL36CjS/rHPu3vovveIJH
mDlauv5b5SdRRF4kk8zHZFIlFJMlT2KcOB3A6doukOVCx+2ikr1v1SbOaDZj1mre
JMf5QKxVEqCR8jT+ak7yx84JB5M=
-----END CERTIFICATE-----

0 comments on commit b11785c

Please sign in to comment.