• If you are still using CentOS 7.9, it's time to convert to Alma 8 with the free centos2alma tool by Plesk or Plesk Migrator. Please let us know your experiences or concerns in this thread:
    CentOS2Alma discussion

Forwarded to devs PHP libcurl fails to populate certinfo array

websavers

Regular Pleskian
TITLE:
PHP libcurl fails to populate certinfo array
PRODUCT, VERSION, OPERATING SYSTEM, ARCHITECTURE:
Plesk Onyx 17.8.11 Update #41 OS: ‪CentOS Linux 7.6.1810 (Core)‬. PHP: 7.2.15. curl curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0
PROBLEM DESCRIPTION:
Use $certinfo = curl_getinfo($ch, CURLINFO_CERTINFO); to get SSL certificate info and the certinfo variable is not populated -- it remains empty. If you also grab the basic info, it shows the array as being empty:

$info = curl_getinfo($ch);
print_r($info);​
STEPS TO REPRODUCE:
Set up a vhost on CentOS 7.5 running nginx direct to php-fpm with PHP v7.2.15 or 7.3.2. Add this to a PHP file in the web root, then visit the URL path:

PHP:
<?php
$domain = 'plesk.com';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://' . $domain);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
#curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_STDERR, 'php://stdout');
curl_setopt($ch, CURLOPT_CERTINFO, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);

if(curl_exec($ch) === false){
    echo '<p>Curl error: ' . curl_error($ch) . "</p>";
}

    $info = curl_getinfo($ch);
    print_r($info);

$certInfo = curl_getinfo($ch, CURLINFO_CERTINFO);

echo "<p>Certificate info for $domain: ";
print_r($certInfo[0]);

echo "</p><p>Test is done</p>";
?>
ACTUAL RESULT:
You'll notice that $certInfo prints as empty.

You can see it in action here: https://clients.websavers.ca/ssl_monitoring_test.php
EXPECTED RESULT:
The $certInfo var should be printing out an array of information about the SSL certificate like this (run on another domain on a non-Plesk server):

Certificate info for mydomain.com: Array ( [Subject] => CN = mydomain.com [Issuer] => C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 [Version] => 2 [Serial Number] => 03c8d112700641145608b3daa7e29fb00f0a [Signature Algorithm] => sha256WithRSAEncryption [Public Key Algorithm] => rsaEncryption [X509v3 Key Usage] => DigitalSignature,KeyEncipherment [X509v3 Extended Key Usage] => TLSWebServerAuthentication,TLSWebClientAuthentication [X509v3 Basic Constraints] => CA:FALSE [X509v3 Subject Key Identifier] => 15:22:31:4A:DD:0F:EB:8F:63:3C:61:42:9E:CC:CE:E1:28:55:C1:F1 [X509v3 Authority Key Identifier] => keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1 [Authority Information Access] => OCSP-URI:http://ocsp.int-x3.letsencrypt.org, CAIssuers-URI:http://cert.int-x3.letsencrypt.org/ [X509v3 Subject Alternative Name] => DNS:mydomain.com,DNS:www.mydomain.com [X509v3 Certificate Policies] => Policy:2.23.140.1.2.1, Policy:1.3.6.1.4.1.44947.1.1.1, CPS:ISRG CPS v2.5 - Let's Encrypt - Free SSL/TLS Certificates [CT Precertificate SCTs] => SignedCertificateTimestamp:, Version:v1(0), LogID:E2:69:4B:AE:26:E8:E9:40:09:E8:86:1B:B6:3B:83:D4:, 3E:E7:FE:74:88:FB:A4:8F:28:93:01:9D:DD:F1:DB:FE, Timestamp:Feb1721:13:14.6172019GMT, Extensions:none, Signature:ecdsa-with-SHA256, 30:45:02:21:00:E5:5E:85:0F:DF:21:36:D8:2E:99:16:, 84:78:61:0C:7F:39:F2:98:71:38:5A:01:07:DF:3B:AD:, 49:70:A3:FC:E7:02:20:1C:9D:8A:18:4D:1D:9E:0A:A6:, 9C:79:BE:4A:0A:B2:6B:01:94:BE:7C:6B:08:A3:42:22:, ED:EC:9E:79:0F:FC:A9, SignedCertificateTimestamp:, Version:v1(0), LogID:63:F2:DB [Start date] => Feb 17 20:13:14 2019 GMT [Expire date] => May 18 20:13:14 2019 GMT [RSA Public Key] => 2048 [rsa(n)] => E2B2353E23839C18F2962A7CF7A417DBB80D1374A40D5E782A67388DFF69D0814A5BACF5910FA3879446FF4D8AD7F114B7ADE9A61EEC19D9D4B83431B2E62BB8EE10CFFCA6828533C5FE55230E53053079169A7E12D9D1EF4E65E56A8316A7AA221C5C8C91B8B88CB361937B7BAE38E03B83B42960EBCA12F697F7D8C3F63585A4C69751E24EFBB9B7A27B6A24F45C922C0AB2E514E35BC5F2A69D63084470543542D965F81F2A0E6D489D69BEE3655BD93A66AC4C3505727057BF89BCF2DBE264D905D6E0DF258516775DFBD07DD95A71DF39872AE594C1C25CA6BF9CAB621A90EDE6FE9F7ECDF389D9463E42F9734406CBF1BFAF83AB9FD628186206AC8C2D [rsa(e)] => 10001 [Signature] => 79:a4:c5:ac:b7:b6:56:5a:81:17:ea:f5:09:6b:b5:03:89:14:46:82:d8:8c:54:3e:8b:27:68:c3:cf:89:80:1e:45:85:7f:7c:28:db:15:b5:d6:da:1d:53:c7:a8:b1:c1:5f:63:f1:d2:49:f5:61:b4:a9:91:dc:64:4c:b2:1e:43:e2:8d:8a:4c:c5:57:6d:e8:92:fe:f0:57:23:c4:c1:b7:13:c1:ee:7f:5e:65:bd:f9:b8:45:f3:57:60:83:df:42:56:b1:31:93:45:16:c7:1d:3d:68:3c:8e:bb:e4:83:0b:b4:a2:03:94:d8:75:51:a2:35:14:14:78:da:ea:35:cd:07:84:4c:2f:80:cf:ec:87:66:27:02:2b:a3:a7:0d:7f:b7:81:1b:4e:06:1b:0e:28:6c:8b:2c:31:25:1c:be:54:ae:89:4e:1a:7d:9b:81:0c:7d:40:97:0f:ec:10:e8:2b:d3:e9:90:66:25:17:80:cf:f1:57:c5:2c:29:1b:c4:c2:21:af:89:0b:83:72:4d:40:2f:1f:b1:4e:8d:fc:d9:4a:fc:b8:3a:a9:33:2e:9d:70:b7:57:5c:95:2d:f9:2d:11:e9:7e:12:cb:14:fa:b9:ac:09:d3:ff:19:a6:45:49:51:e3:87:c9:b3:e5:94:7c:cf:2b:93:31:75:02:0f:de:96: [Cert] => -----BEGIN CERTIFICATE----- MIIFXjCCBEagAwIBAgISA8jREnAGQRRWCLPap+KfsA8KMA0GCSqGSIb3DQEBCwUA MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTAyMTcyMDEzMTRaFw0x OTA1MTgyMDEzMTRaMBYxFDASBgNVBAMTC3NjaGVsZXcuY29tMIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4rI1PiODnBjylip896QX27gNE3SkDV54Kmc4 jf9p0IFKW6z1kQ+jh5RG/02K1/EUt63pph7sGdnUuDQxsuYruO4Qz/ymgoUzxf5V Iw5TBTB5Fpp+EtnR705l5WqDFqeqIhxcjJG4uIyzYZN7e6444DuDtClg68oS9pf3 2MP2NYWkxpdR4k77ubeie2ok9FySLAqy5RTjW8Xypp1jCERwVDVC2WX4HyoObUid ab7jZVvZOmasTDUFcnBXv4m88tviZNkF1uDfJYUWd1370H3ZWnHfOYcq5ZTBwlym v5yrYhqQ7eb+n37N84nZRj5C+XNEBsvxv6+Dq5/WKBhiBqyMLQIDAQABo4ICcDCC AmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQVIjFK3Q/rj2M8YUKezM7hKFXB8TAf BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv MCcGA1UdEQQgMB6CC3NjaGVsZXcuY29tgg93d3cuc2NoZWxldy5jb20wTAYDVR0g BEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0 cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8A dgDiaUuuJujpQAnohhu2O4PUPuf+dIj7pI8okwGd3fHb/gAAAWj9TlB5AAAEAwBH MEUCIQDlXoUP3yE22C6ZFoR4YQx/OfKYcThaAQffO61JcKP85wIgHJ2KGE0dngqm nHm+SgqyawGUvnxrCKNCIu3snnkP/KkAdQBj8tvN6DvMLM8LcoQnV2szpI1hd4+9 daY4scdoVEvYjQAAAWj9TlB5AAAEAwBGMEQCIDH8qdsdJdJ0vrWyDpmRAkER6q4V PW77k/fNjZkdvvlLAiArlBcvw1ZG/ZTiQrvlCUqPF+6wrcI0m3qzcu98yu31zjAN BgkqhkiG9w0BAQsFAAOCAQEAeaTFrLe2VlqBF+r1CWu1A4kURoLYjFQ+iydow8+J gB5FhX98KNsVtdbaHVPHqLHBX2Px0kn1YbSpkdxkTLIeQ+KNikzFV23okv7wVyPE wbcTwe5/XmW9+bhF81dgg99CVrExk0UWxx09aDyOu+SDC7SiA5TYdVGiNRQUeNrq Nc0HhEwvgM/sh2YnAiujpw1/t4EbTgYbDihsiywxJRy+VK6JThp9m4EMfUCXD+wQ 6CvT6ZBmJReAz/FXxSwpG8TCIa+JC4NyTUAvH7FOjfzZSvy4OqkzLp1wt1dclS35 LRHpfhLLFPq5rAnT/xmmRUlR44fJs+WUfM8rkzF1Ag/elg== -----END CERTIFICATE----- )
ANY ADDITIONAL INFORMATION:
This issue came about while using WHMCS -- the latest release of the software attempts to get SSL certificate info from domains and indicate if there's any problems and that they should set up an SSL cert.

The WHMCS techs said this about this issue: 'this is a bug with the PHP build or "curl" extension itself'

Note that certinfo *is* shown if you run curl via command line.
YOUR EXPECTATIONS FROM PLESK SERVICE TEAM:
Help with sorting out
 
On CentOS 7 during PHP 7.3 configure:
Code:
configure:23997: checking for cURL support
configure:24033: result: yes, shared
configure:24086: checking for libcurl.pc
configure:24090: result: using default path
configure:24109: checking for cURL 7.15.5 or greater
configure:24113: result: 7.29.0
configure:24304: checking for SSL support in libcurl
configure:24307: result: yes
...
configure:24455: checking for openssl support in libcurl
configure:24483: ccache gcc -o conftest    -lcurl   conftest.c -lbz2 -lz -lrt -lm -ldl -lnsl  -lxml2 -lz -lm -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto >&5
configure:24483: $? = 0
configure:24483: ./conftest
configure:24483: $? = 255
configure: program exited with status 255
configure: failed program was:
| /* confdefs.h */
| #...
| #define HAVE_CURL_SSL 1
| /* end confdefs.h.  */
|
| #include <strings.h>
| #include <curl/curl.h>
|
| int main(int argc, char *argv[])
| {
|   curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
|
|   if (data && data->ssl_version && *data->ssl_version) {
|     const char *ptr = data->ssl_version;
|
|     while(*ptr == ' ') ++ptr;
|     return strncasecmp(ptr, "OpenSSL", sizeof("OpenSSL")-1);
|   }
|   return 1;
| }
|
configure:24506: result: no
configure:24515: checking for gnutls support in libcurl
configure:24543: ccache gcc -o conftest    -lcurl   conftest.c -lbz2 -lz -lrt -lm -ldl -lnsl  -lxml2 -lz -lm -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto >&5
configure:24543: $? = 0
configure:24543: ./conftest
configure:24543: $? = 7
configure: program exited with status 7
configure: failed program was:
| /* confdefs.h */
| #...
| #define HAVE_CURL_SSL 1
| /* end confdefs.h.  */
|
| #include <strings.h>
| #include <curl/curl.h>
|
| int main(int argc, char *argv[])
| {
|   curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
|
|   if (data && data->ssl_version && *data->ssl_version) {
|     const char *ptr = data->ssl_version;
|
|     while(*ptr == ' ') ++ptr;
|     return strncasecmp(ptr, "GnuTLS", sizeof("GnuTLS")-1);
|   }
|   return 1;
| }
|
configure:24560: result: no
configure:24673: checking for curl_easy_perform in -lcurl
configure:24698: ccache gcc -o conftest -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -mtune=generic -fno-strict-aliasing -pipe -Wno-all -fvisibility=hidden  -Wl,--enable-new-dtags  -lcurl conftest.c -lcurl  -lbz2 -lz -lrt -lm -ldl -lnsl  -lxml2 -lz -lm -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto >&5
configure:24698: $? = 0
configure:24707: result: yes
configure:24828: checking for curl_easy_strerror in -lcurl
configure:24853: ccache gcc -o conftest -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -mtune=generic -fno-strict-aliasing -pipe -Wno-all -fvisibility=hidden  -Wl,--enable-new-dtags  -lcurl conftest.c -lcurl  -lbz2 -lz -lrt -lm -ldl -lnsl  -lxml2 -lz -lm -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto >&5
configure:24853: $? = 0
configure:24862: result: yes
configure:24981: checking for curl_multi_strerror in -lcurl
configure:25006: ccache gcc -o conftest -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -mtune=generic -fno-strict-aliasing -pipe -Wno-all -fvisibility=hidden  -Wl,--enable-new-dtags  -lcurl conftest.c -lcurl  -lbz2 -lz -lrt -lm -ldl -lnsl  -lxml2 -lz -lm -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto >&5
configure:25006: $? = 0
configure:25015: result: yes
...
ac_cv_lib_curl_curl_easy_perform=yes
ac_cv_lib_curl_curl_easy_strerror=yes
ac_cv_lib_curl_curl_multi_strerror=yes

Code:
$ curl --version
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.28.4 zlib/1.2.7 libidn/1.28 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz unix-sockets

On the other hand, on Debian 8:
Code:
$ curl --version
curl 7.38.0 (x86_64-pc-linux-gnu) libcurl/7.38.0 OpenSSL/1.0.1t zlib/1.2.8 libidn/1.29 libssh2/1.4.3 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API SPNEGO NTLM NTLM_WB SSL libz TLS-SRP

On CentOS 7:

  1. Take a relevant example from libcurl documentation: cp /usr/share/doc/libcurl-devel-7.29.0/certinfo.c .
  2. Edit certinfo.c to match PHP script (replace URL with https://plesk.com)
  3. Compile: gcc -l curl certinfo.c
  4. Run: ./a.out
Code:
0 certs!

On Debian 8 the same steps result in:

Code:
4 certs!
Subject:OU=Domain Control Validated; OU=PositiveSSL; CN=plesk.com
Issuer:C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO RSA Domain Validation Secure Server CA
Version:2
Signature Algorithm:sha256WithRSAEncryption
Start date:2016-12-19 00:00:00 GMT
Expire date:2019-12-19 23:59:59 GMT
Public Key Algorithm:rsaEncryption
RSA Public Key:2048
rsa(n):ac:2d:d6:4a:4c:f2:17:02:8b:0f:cf:c4:1f:bd:7f:fd:e2:a8:00:07:94:56:fc:69:17:2b:d7:05:7a:ef:eb:0f:4f:d9:c8:be:b1:8f:fc:40:2b:a0:08:2f:56:1c:91:c5:fe:3e:19:55:d8:c3:6e:a3:c5:f0:9c:8e:1c:f4:b8:89:56:8b:ad:bd:cc:8d:0c:dc:c3:72:62:b9:3c:b0:4b:b8:09:92:3b:f3:c7:df:b2:f1:30:cb:32:b8:64:52:54:29:94:db:da:d3:f9:7b:0a:30:f9:67:2c:57:29:58:8a:d8:da:60:c5:01:9e:4d:9e:ec:36:ec:aa:08:1a:03:ef:37:c2:93:b5:13:37:bf:f0:b6:fc:a8:b9:6f:c6:b2:2c:02:84:9c:e0:d3:ef:55:84:b1:a8:b8:5d:a4:73:6f:ee:07:a6:03:98:a1:6d:f9:96:e1:bc:d2:6d:6b:4f:fe:7a:92:7a:98:00:9a:b9:bc:6d:cf:63:8e:8b:58:da:22:7e:08:c2:07:3d:17:a5:ed:64:af:a8:7f:f0:16:9d:37:87:06:01:5b:2d:b9:e4:74:08:57:eb:5a:87:81:c2:30:3e:ca:23:a2:81:97:f5:8f:7d:fc:60:7d:ca:2a:7c:bc:3a:91:0f:a1:45:6c:58:0f:36:71:d9:6b:d3:ca:0c:28:e6:61:
rsa(e):01:00:01:
...

This means that this is a system libcurl issue, rather than Plesk PHP issue. Currently we have no plans to support our own build of libcurl for use with client PHP versions.
I suspect that this is due to system libcurl being linked against NSS instead of OpenSSL, but I'm not 100% sure and official documentation somewhat contradicts this.

As a workaround I suggest finding another way to obtain the required information from PHP, w/o using curl. Something like this should likely work. Feel free to tune to your needs.

Code:
<?php
$url = "https://plesk.com";
$orignal_parse = parse_url($url, PHP_URL_HOST);
$get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE)));
$read = stream_socket_client("ssl://".$orignal_parse.":443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $get);
$cert = stream_context_get_params($read);
$certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']);
var_dump($certinfo);
openssl_x509_export($cert['options']['ssl']['peer_certificate'], $body);
print $body;
 
Thanks for doing that investigation @IgorG !

So it sounds like our best way forward is to:

1. Open a bug report with the RedHat dev team here as this is ultimately a bug in the build of libcurl for RedHat Enterprise 7 that should be fixed
2. Request that WHMCS use an alternate mechanism for obtaining certs similar to the code you provided, either in the event of libcurl failure like this, or as a replacement lookup mechanism.

I'll take care of #2. You guys must regularly report things like this to RedHat -- is it best to submit direct to them or on the CentOS bug tracker? I feel like the latter would just say to report it to RedHat since it's an upstream library issue, no?
 
Back
Top