Skip to content

Commit b11785c

Browse files
camporternikic
authored andcommitted
Fixed bug #81085: Add version 7.71.0 blob options.
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.
1 parent aad0d26 commit b11785c

8 files changed

+453
-0
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ PHP NEWS
55
- Core:
66
. Fixed bug #81202 (powerpc64 build fails on fibers). (krakjoe)
77

8+
- Curl:
9+
. Fixed bug #81085 (Support CURLOPT_SSLCERT_BLOB for cert strings).
10+
(camporter)
11+
812
- Reflection:
913
. Fixed bug #81200 (no way to determine if Closure is static). (krakjoe)
1014

UPGRADING

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,16 @@ PHP 8.1 UPGRADE NOTES
193193

194194
- Curl:
195195
. Added CURLOPT_DOH_URL option.
196+
. Added certificate blob options when for libcurl >= 7.71.0:
197+
198+
CURLOPT_ISSUERCERT_BLOB
199+
CURLOPT_PROXY_ISSUERCERT
200+
CURLOPT_PROXY_ISSUERCERT_BLOB
201+
CURLOPT_PROXY_SSLCERT_BLOB
202+
CURLOPT_PROXY_SSLKEY_BLOB
203+
CURLOPT_SSLCERT_BLOB
204+
CURLOPT_SSLKEY_BLOB
205+
196206
. Added CURLStringFile, which can be used to post a file from a string rather
197207
than a file:
198208

ext/curl/interface.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,16 @@ PHP_MINIT_FUNCTION(curl)
11531153
REGISTER_CURL_CONSTANT(CURL_VERSION_ALTSVC);
11541154
#endif
11551155

1156+
#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
1157+
REGISTER_CURL_CONSTANT(CURLOPT_ISSUERCERT_BLOB);
1158+
REGISTER_CURL_CONSTANT(CURLOPT_PROXY_ISSUERCERT);
1159+
REGISTER_CURL_CONSTANT(CURLOPT_PROXY_ISSUERCERT_BLOB);
1160+
REGISTER_CURL_CONSTANT(CURLOPT_PROXY_SSLCERT_BLOB);
1161+
REGISTER_CURL_CONSTANT(CURLOPT_PROXY_SSLKEY_BLOB);
1162+
REGISTER_CURL_CONSTANT(CURLOPT_SSLCERT_BLOB);
1163+
REGISTER_CURL_CONSTANT(CURLOPT_SSLKEY_BLOB);
1164+
#endif
1165+
11561166
REGISTER_CURL_CONSTANT(CURLOPT_SAFE_UPLOAD);
11571167

11581168
#ifdef PHP_CURL_NEED_OPENSSL_TSL
@@ -2520,6 +2530,9 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i
25202530
#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */
25212531
case CURLOPT_PROXY_TLS13_CIPHERS:
25222532
case CURLOPT_TLS13_CIPHERS:
2533+
#endif
2534+
#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
2535+
case CURLOPT_PROXY_ISSUERCERT:
25232536
#endif
25242537
{
25252538
zend_string *tmp_str;
@@ -2923,6 +2936,29 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i
29232936
ZVAL_COPY(&ch->handlers.fnmatch->func_name, zvalue);
29242937
break;
29252938

2939+
/* Curl blob options */
2940+
#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
2941+
case CURLOPT_ISSUERCERT_BLOB:
2942+
case CURLOPT_PROXY_ISSUERCERT_BLOB:
2943+
case CURLOPT_PROXY_SSLCERT_BLOB:
2944+
case CURLOPT_PROXY_SSLKEY_BLOB:
2945+
case CURLOPT_SSLCERT_BLOB:
2946+
case CURLOPT_SSLKEY_BLOB:
2947+
{
2948+
zend_string *tmp_str;
2949+
zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);
2950+
2951+
struct curl_blob stblob;
2952+
stblob.data = ZSTR_VAL(str);
2953+
stblob.len = ZSTR_LEN(str);
2954+
stblob.flags = CURL_BLOB_COPY;
2955+
error = curl_easy_setopt(ch->cp, option, &stblob);
2956+
2957+
zend_tmp_string_release(tmp_str);
2958+
}
2959+
break;
2960+
#endif
2961+
29262962
default:
29272963
if (is_array_config) {
29282964
zend_argument_value_error(2, "must contain only valid cURL options");

ext/curl/tests/curl_setopt_ssl.phpt

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
--TEST--
2+
CURLOPT_SSL* basic client auth tests
3+
--EXTENSIONS--
4+
curl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("proc_open")) die("skip no proc_open");
8+
exec('openssl version', $out, $code);
9+
if ($code > 0) die("skip couldn't locate openssl binary");
10+
if (PHP_OS_FAMILY === 'Windows') die('skip not for Windows');
11+
$curl_version = curl_version();
12+
if ($curl_version['version_number'] < 0x074700) {
13+
die("skip: blob options not supported for curl < 7.71.0");
14+
}
15+
?>
16+
--FILE--
17+
<?php
18+
19+
function check_error(CurlHandle $ch) {
20+
if (curl_errno($ch) !== 0) {
21+
echo "CURL ERROR: " . curl_errno($ch) . "\n";
22+
}
23+
}
24+
25+
function check_response($response, $clientCertSubject) {
26+
if (strpos($response, $clientCertSubject) === false) {
27+
echo "client cert subject not in response\n";
28+
} else {
29+
echo "client cert subject in response\n";
30+
}
31+
}
32+
33+
$clientCertSubject = "Subject: C=US, ST=TX, L=Clientlocation, O=Clientcompany, CN=clientname/[email protected]";
34+
35+
// load server cert
36+
$serverCertPath = __DIR__ . DIRECTORY_SEPARATOR . 'curl_setopt_ssl_servercert.pem';
37+
$serverCert = file_get_contents($serverCertPath);
38+
39+
// load server key
40+
$serverKeyPath = __DIR__ . DIRECTORY_SEPARATOR . 'curl_setopt_ssl_serverkey.pem';
41+
$serverKey = file_get_contents($serverKeyPath);
42+
43+
// load client cert
44+
$clientCertPath = __DIR__ . DIRECTORY_SEPARATOR . 'curl_setopt_ssl_clientcert.pem';
45+
$clientCert = file_get_contents($clientCertPath);
46+
47+
// load client key
48+
$clientKeyPath = __DIR__ . DIRECTORY_SEPARATOR . 'curl_setopt_ssl_clientkey.pem';
49+
$clientKey = file_get_contents($clientKeyPath);
50+
51+
if ($serverCert === false
52+
|| $serverKey === false
53+
|| $clientCert === false
54+
|| $clientKey === false
55+
) {
56+
die('failed to load test certs and keys for files');
57+
}
58+
59+
$port = 14430;
60+
61+
// set up local server
62+
$cmd = "openssl s_server -key $serverKeyPath -cert $serverCertPath -accept $port -www -CAfile $clientCertPath -verify_return_error -Verify 1";
63+
$process = proc_open($cmd, [["pipe", "r"], ["pipe", "w"], ["pipe", "w"]], $pipes);
64+
65+
if ($process === false) {
66+
die('failed to start server');
67+
}
68+
try {
69+
// Give the server time to start
70+
sleep(1);
71+
72+
echo "case 1: client cert and key from string\n";
73+
$ch = curl_init("https://127.0.0.1:$port/");
74+
var_dump(curl_setopt($ch, CURLOPT_SSLCERT_BLOB, $clientCert));
75+
var_dump(curl_setopt($ch, CURLOPT_SSLKEY_BLOB, $clientKey));
76+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false));
77+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
78+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
79+
80+
$response = curl_exec($ch);
81+
check_response($response, $clientCertSubject);
82+
check_error($ch);
83+
curl_close($ch);
84+
85+
echo "\n";
86+
echo "case 2: empty client cert and key from string\n";
87+
$ch = curl_init("https://127.0.0.1:$port/");
88+
var_dump(curl_setopt($ch, CURLOPT_SSLCERT_BLOB, ''));
89+
var_dump(curl_setopt($ch, CURLOPT_SSLKEY_BLOB, $clientKey));
90+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false));
91+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
92+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
93+
94+
$response = curl_exec($ch);
95+
check_response($response, $clientCertSubject);
96+
check_error($ch);
97+
curl_close($ch);
98+
99+
echo "\n";
100+
echo "case 3: client cert and empty key from string\n";
101+
$ch = curl_init("https://127.0.0.1:$port/");
102+
var_dump(curl_setopt($ch, CURLOPT_SSLCERT_BLOB, $clientCert));
103+
var_dump(curl_setopt($ch, CURLOPT_SSLKEY_BLOB, ''));
104+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false));
105+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
106+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
107+
108+
$response = curl_exec($ch);
109+
check_response($response, $clientCertSubject);
110+
check_error($ch);
111+
curl_close($ch);
112+
113+
echo "\n";
114+
echo "case 4: client cert and key from file\n";
115+
$ch = curl_init("https://127.0.0.1:$port/");
116+
var_dump(curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath));
117+
var_dump(curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath));
118+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false));
119+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
120+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
121+
122+
$response = curl_exec($ch);
123+
check_response($response, $clientCertSubject);
124+
check_error($ch);
125+
curl_close($ch);
126+
127+
echo "\n";
128+
echo "case 5: issuer cert from file\n";
129+
$ch = curl_init("https://127.0.0.1:$port/");
130+
var_dump(curl_setopt($ch, CURLOPT_CAINFO, $serverCertPath));
131+
var_dump(curl_setopt($ch, CURLOPT_ISSUERCERT, $serverCertPath));
132+
var_dump(curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath));
133+
var_dump(curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath));
134+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true));
135+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
136+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
137+
138+
$response = curl_exec($ch);
139+
check_response($response, $clientCertSubject);
140+
check_error($ch);
141+
curl_close($ch);
142+
143+
echo "\n";
144+
echo "case 6: issuer cert from string\n";
145+
$ch = curl_init("https://127.0.0.1:$port/");
146+
var_dump(curl_setopt($ch, CURLOPT_CAINFO, $serverCertPath));
147+
var_dump(curl_setopt($ch, CURLOPT_ISSUERCERT_BLOB, $serverCert));
148+
var_dump(curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath));
149+
var_dump(curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath));
150+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true));
151+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
152+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
153+
154+
$response = curl_exec($ch);
155+
check_response($response, $clientCertSubject);
156+
check_error($ch);
157+
curl_close($ch);
158+
159+
echo "\n";
160+
echo "case 7: empty issuer cert from string\n";
161+
$ch = curl_init("https://127.0.0.1:$port/");
162+
var_dump(curl_setopt($ch, CURLOPT_CAINFO, $serverCertPath));
163+
var_dump(curl_setopt($ch, CURLOPT_ISSUERCERT_BLOB, ''));
164+
var_dump(curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath));
165+
var_dump(curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath));
166+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true));
167+
var_dump(curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false));
168+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
169+
170+
$response = curl_exec($ch);
171+
check_response($response, $clientCertSubject);
172+
check_error($ch);
173+
curl_close($ch);
174+
175+
} finally {
176+
// clean up server process
177+
proc_terminate($process);
178+
proc_close($process);
179+
}
180+
181+
?>
182+
--EXPECT--
183+
case 1: client cert and key from string
184+
bool(true)
185+
bool(true)
186+
bool(true)
187+
bool(true)
188+
client cert subject in response
189+
190+
case 2: empty client cert and key from string
191+
bool(true)
192+
bool(true)
193+
bool(true)
194+
bool(true)
195+
client cert subject not in response
196+
CURL ERROR: 58
197+
198+
case 3: client cert and empty key from string
199+
bool(true)
200+
bool(true)
201+
bool(true)
202+
bool(true)
203+
client cert subject not in response
204+
CURL ERROR: 58
205+
206+
case 4: client cert and key from file
207+
bool(true)
208+
bool(true)
209+
bool(true)
210+
bool(true)
211+
client cert subject in response
212+
213+
case 5: issuer cert from file
214+
bool(true)
215+
bool(true)
216+
bool(true)
217+
bool(true)
218+
bool(true)
219+
bool(true)
220+
client cert subject in response
221+
222+
case 6: issuer cert from string
223+
bool(true)
224+
bool(true)
225+
bool(true)
226+
bool(true)
227+
bool(true)
228+
bool(true)
229+
client cert subject in response
230+
231+
case 7: empty issuer cert from string
232+
bool(true)
233+
bool(true)
234+
bool(true)
235+
bool(true)
236+
bool(true)
237+
bool(true)
238+
client cert subject not in response
239+
CURL ERROR: 83
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIFgDCCA2gCCQCpI/KBeVlPsjANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMC
3+
VVMxCzAJBgNVBAgMAlRYMRcwFQYDVQQHDA5DbGllbnRsb2NhdGlvbjEWMBQGA1UE
4+
CgwNQ2xpZW50Y29tcGFueTETMBEGA1UEAwwKY2xpZW50bmFtZTEfMB0GCSqGSIb3
5+
DQEJARYQdGVzdEBleGFtcGxlLmNvbTAeFw0yMTA2MjYxOTAyMTlaFw00ODExMTAx
6+
OTAyMTlaMIGBMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVFgxFzAVBgNVBAcMDkNs
7+
aWVudGxvY2F0aW9uMRYwFAYDVQQKDA1DbGllbnRjb21wYW55MRMwEQYDVQQDDApj
8+
bGllbnRuYW1lMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMIICIjAN
9+
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2TlNc7TMAjjjgERln914ScxiMcwH
10+
5TVOjUxhBX9XuSNP8tnO03r4w6bQFCaq7ire63ssCx1aua4tCIMeKG++ueiMeqLc
11+
fmc5lIs3GuMEYrcopf9FQvvDuRM8EpkIKNNp5e82TBQkEAtSFc1e8rPnjAxi68gm
12+
g/yTrm74tG4sqyHtzpiNUwwP4sE3yfV6sENF+Sth8grfsTBShXmq1eQ+PWYBHT13
13+
e4J0YBkOXduNIFuXs/mKUgFlEhzhthdznsN2dLzWzVmZZGX1nTsZ+yQYDdGVq0ay
14+
F20unovhZNh2J9kfdgKDZFPgtRHgFYZZTeacZCGWg9KMkwVXqdufjk4M/khkpwTh
15+
agZHhf1lLq3pmGzm8hZto5OKWXOk+hoopSEU0+V+PJtl5Cl7MIa6gJVsk6rT3abp
16+
X5e3bmlABreZIlFjk2kO68gNbbSdZiZfjqtqLeTY2KwYsUZ358zAJlDT8Zc0T00u
17+
pQEBQxFBNWQqfXwlnWtvcKpvwonbBZLcUGoWMiuFIgsnsTvxLWayX20Njy2m7K7d
18+
002cn3KBVpu3EXO9NkYKg+q6z79FvAlf9Xq/nV9ubvGQ/wEpJOr4egvWHoC0qt3/
19+
uW+nzhmqhBv6cGWnKDUVWknAsothJlunP55Dtq2fmxmXH4TjYqofhfvMN88XdxFb
20+
0ZUYojirWTOSIn8CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAa+rI3YH3HT+KiyBE
21+
8Mv7/gH1jlQ2zPzTu0EccgnDcuXQSlAtvz+0evoUYOpREQ/lfeNeICllJgK0R0Go
22+
QReqsnC6Tbq/pCDMmo6g6vm7H0j1b1RNNnhNuh0el/O/cnAT6KBcrmojBBkJMHRT
23+
vHJeE0W+rr3Qvg1GM3KfHlyxQeZLQrTeuNjE5f+9LUy1d11UV2xLuiGax5zYQQM3
24+
iwqHFvfe3EYzmRECmMY4qLZgN/c1Cd4RK8wEPKKsvOjxpNLAAR8+VeLUj32UmtWd
25+
ZFnTFEiJI0RWtRdLt1J3r9+ybg3c0e/BJWxK6Dw00NV6A5UNd5lVbv5v9ZMGI7tu
26+
fTaZE4AR50yd+VMZXMMZj282eZOIIoi1IeT1uofyDcYvxWjNn+gW/Di8niKunwYA
27+
tdXANcGtzlB3slBSX0gZ9sItdFkb7gDG86L7Gg90GJEG0ZtU8WzQ5BUArMfYWZbv
28+
l8A0kOQi6dR+/RjcswRDXWSYx3U0j0db5Ibg7D3WMRcHQasck2YHZrlku5ILC5Gd
29+
F0N4bIps2V6kN/OWFdSyWTwvIS2m62ovg3o9hR2aODqkL36CjS/rHPu3vovveIJH
30+
mDlauv5b5SdRRF4kk8zHZFIlFJMlT2KcOB3A6doukOVCx+2ikr1v1SbOaDZj1mre
31+
JMf5QKxVEqCR8jT+ak7yx84JB5M=
32+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)