1

I am trying to contact a url via php's stream_socket_client() command which is failing with no error code or msg. Does this use openssl, curl or something else? The site uses https and works fine from a web browser.

enter image description here

When I call openssl directly it connects but appears unahppy with my certificate chain.

root@drupal7 drupal7/includes# openssl s_client -connect test.cgps.org:443
CONNECTED(00000003)
Verify return code: 21 (unable to verify the first certificate)

Same with curl

curl https://test.cgps.org/?q=/admin/config/search/clean-urls/check
curl: (60) SSL certificate problem: unable to get local issuer certificate

So I figured I needed to install the intermediate cert into Linux which I did by placing the same intermediate cert Apache uses into /usr/local/share/ca-certificates and running update-ca-certificates.

root@drupal7 drupal7/includes# update-ca-certificates
Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.

Still no luck...

AdamG
  • 2,570
  • 3
  • 24
  • 35
  • 1
    "Does this use openssl, curl, or something else?" -- Something else entirely, and not just a little different; very different. You're asking, "is this food an apple, a pear, or cheese?" when the answer is, you're trying to eat a crowbar. You don't use socket streams to download web pages, you use it to set up bi-directional TCP connections, which, if you're clever, you might be able to use to get web content, just like you can use a crowbar to pull apples and pears from the tree, or beat open a cheese wheel. – Ghedipunk Oct 13 '15 at 20:49
  • Ok, but could stream_socket_client be failing for the same reason, not happy with the cert? Is there any way to troubleshoot stream_socket_client failures further? – AdamG Oct 13 '15 at 21:18
  • `stream_socket_client()` operates at the session layer, certs don't start mattering until the application layer, so no, there is absolutely no reason why the cert would matter. Most likely, the connection is getting timed out by the web server waiting for a response to the TLS handshake, which means that it successfully connects, successfully receives a packet, and the connection successfully gets closed 5 seconds later, thus no errors from the point of view of `stream_socket_client()`. – Ghedipunk Oct 13 '15 at 21:25
  • 1
    And, to make this more obvious... If you want to download HTTPS content, `stream_socket_client()` is far, far, far too low level for your purposes. You would have to write your own TLS client on top of the socket stream, PLUS your own HTTP client before you start getting close to where you want. Use cURL. Figure out what's wrong with the cert or the authority chain. – Ghedipunk Oct 13 '15 at 21:28
  • @Ghedipunk `stream_socket_client('tls://example.com:443', ...)` works just fine. Then you just read and write your data as if it wasn't encrypted. No need to your own TLS client?! – Alexis Wilke Nov 03 '17 at 03:17
  • Maybe that answer could have helped? https://stackoverflow.com/questions/24928311 – Alexis Wilke Nov 03 '17 at 03:18

1 Answers1

1

Despite a statement made in the comments saying PHP's steam_socket_client() is too low-level to care about certificates, I know it does care because I have observed it returning false on systems that lack some certificate in the standard location, where providing a stream-context (to tell it where else to look for certs) stops this from happening.

Frustratingly, the $errno can remain 0 until you've fixed this, so you are better off checking your certificate-authority directory first with another tool.

diagnosis

via curl

To get things working with curl, you can usually figure out what's going on by adding the --verbose flag. This should tell you which concatenated PEM-file of certificates (CAfile/cacert) and CA-directory (capath) are being used, if any. Toward the top of the output, you'll get something like:

[...]
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: /home2/wynddorg/private_html/trusted_roots
[...]

Once you know what these are defaulting to, you can either adjust your system configuration or simply add either --cacert <concatenated_cert_path> flag or --capath <c_rehash'd_directory> or both.

via openssl s_client

Using s_client is a bit trickier; you may have access to its -trace option depending on your version and how it was compiled instead, but try:

strace -e open,stat openssl s_client -connect test.cgps.org:443 [-CApath <dir>]
# this trick provided by the amazing xemdetia in irc://freenode/%23%23openssl

About 5 lines after the CONNECTED(00000003) message, you should see an open("...", O_RDONLY) (or several) which should reveal which locations are being used to find certs.


I'm missing one or more certs. Now what?

Once you know which certificates are missing, you can download them as shown here, and then do any:

  • concatenate them into a PEM file (preferably in order) and then specify that file when you try to connect to your site
  • save them as individual '.crt'-files in the default directory for this and then run c_rehash on that directory (likely requires root/sudo)
  • save them as individual '.crt'-files in another directory and explicitly specify that as the capath when connecting. You may wish to do this on shared hosting or if you don't want all applications to trust the certs, just certain ones.

Once you have things working with openssl s_client or curl or both, you can specify the same options like so:

$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'cafile', <concatenated_cert_file_path>);
stream_context_set_option($context, 'ssl', 'capath', <c_rehashed_cert_dir_path>);
// You only really need to provide one of the two options above. Using capath is
// faster, but this only matters if you'll be making a huge number of
// connections.
$ssc = stream_socket_client(
    'ssl://subdomain.example.com:443',
    $errno,
    $errstr,
    30,
    STREAM_CLIENT_CONNECT,
    $context
);
if ($ssc) { /* ... */  }
koyae
  • 720
  • 7
  • 12