72

I just recently upgraded to OSX 10.10 Yosemite and I since the upgrade I can't do Curl POST to a SSL url anymore.

I first used wordpress's wp_remote_request call and also tried to use curl in php. Both (as expected) give the same error message:

Error Number:56

Error String:SSLRead() return error -9806

Note: when I curl POST to HTTP it works fine. I reckon it is a setting in PHP.ini or in my apache (I lost my original HTTPD.conf file after upgrade...).

Can anyone help me out?

Asaph
  • 159,146
  • 25
  • 197
  • 199
Mattijs
  • 3,265
  • 3
  • 38
  • 35
  • 5
    why down vote??? It is a real issue I am experiencing and can't find any good solution in Google – Mattijs Oct 22 '14 at 00:58
  • To clarify: I curl POST from my local MB to an external HTTPS APi – Mattijs Oct 24 '14 at 04:01
  • 1
    You probably got the downvote because although this is a perfectly reasonable question, it is off topic for SO - it should really be on Serverfault. – Synchro Feb 02 '15 at 12:23
  • 12
    In Mattijs's defense, he's coding a call to `wp_remote_request()` in PHP which relies on cURL underneath. I think that it's valid that he asks on SO since his proximal issue started with coding. It's only after knowing that the answer that he could know that the question probably would've been better on ServerFault – Kirby May 21 '15 at 17:55
  • 1
    thanks buddy. that is exactly how I intended it. – Mattijs May 23 '15 at 00:18
  • 1
    @Mattijs I got these errors on my 2017 MBP as well. Same solution worked for me as well. – mishka Sep 18 '17 at 00:02
  • Glad you got it resolved! – Mattijs Oct 08 '17 at 00:21

3 Answers3

142

I've seen this error happen when php is compiled with a version of cURL that uses Apple's Secure Transport under Yosemite and the target of the URL request doesn't support SSLv3 (which was probably disabled due to the POODLE vulnerability). What is the output of this command?

$ php -i | grep "SSL Version"

I suspect you'll see this:

SSL Version => SecureTransport

You can overcome this by installing a version of php which uses a version of cURL which uses OpenSSL instead of SecureTransport. This is most easily done with homebrew. So install that first if you don't already have it. If homebrew is installed but you haven't run brew update since upgrading to Yosemite, do that first. Also make sure you've installed XCode >= 6.1 and the latest XCode command line tools. brew doctor will tell you if you've done it all right.

Add the Homebrew taps below that you will need in order to get brewed php installed. Skip this step if these repos are already tapped. If you're unsure if these repos are already tapped, just run the commands below. Worst case scenario, you'll get a harmless Warning: Already tapped!

$ brew tap homebrew/dupes
$ brew tap homebrew/versions
$ brew tap homebrew/php

Then install curl with openssl:

$ brew install --with-openssl curl

Then install php using the curl you just installed and brewed openssl:

$ brew install --with-homebrew-curl --with-httpd24 php55
  • if using apache, make sure to add LoadModule php5_module /usr/local/opt/php55/libexec/apache2/libphp5.so to your /etc/apache2/httpd.conf and restart apache.

  • if not using apache 2.4, you can remove --with-httpd24 from the above command.

  • if using nginx, follow the caveat instuctions for starting fpm:

    To launch php-fpm on startup:

    mkdir -p ~/Library/LaunchAgents
    cp /usr/local/opt/php55/homebrew.mxcl.php55.plist ~/Library/LaunchAgents/
    launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.php55.plist
    

Install any php extensions you're going to need eg. mcrypt.

$ brew install php55-mcrypt

After you're done, run this again:

$ php -i | grep "SSL Version"

And you should see:

SSL Version => OpenSSL/1.0.2h

And now, re-test your application and the SSLRead() return error -9806 should go away.

Asaph
  • 159,146
  • 25
  • 197
  • 199
  • 2
    Hi Asaph, Yes, I have SSL Version => SecureTransport. I have Brew, just wondering if installing with Brew will overwrite my current php, or will it add another PHP version alongside so that I have to disable to stock version? Also Thanks for your thorough answer. I would not have found out myself, to specific a problem – Mattijs Oct 24 '14 at 03:57
  • this is the openssl info I got: `New, TLSv1/SSLv3, Cipher is RC4-MD5 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1 Cipher : RC4-MD5 Session-ID: 0B220000E93AF2E279F784D25D6FC08675E63F983424A4296BEBE59AF89F3E7C Session-ID-ctx: Master-Key: 4B0BFE2ECC5624D0E3A2AD44FF6DC30F25E0C4889C6CA5EF0D0E90C1469D70C9D6B5321A4B2C1A084355A79A013C4420 Key-Arg : None Start Time: 1414123290 Timeout : 300 (sec) Verify return code: 0 (ok)` – Mattijs Oct 24 '14 at 04:02
  • @Mattijs Homebrew installs all software to `/usr/local` and doesn't touch OSX's php. As long as `/usr/local/bin` appears before `/usr/bin` in your system's `$PATH` environment variable, the system will use brew's version of php. If you uninstall brew's php, then your system goes back to using the php binary in `/usr/bin`. – Asaph Oct 24 '14 at 05:14
  • 1
    Hi Asaph, I installed the BREW PHP version, it all went fine after following the brew Doctor's suggestions. The SSL call is working now using PHP, (wordpress wp_remote_request). However, when I curl in the terminal to the same enpoint, i see the -9806 again:`* SSLRead() return error -9806 * Closing connection 0 curl: (56) SSLRead() return error -9806`. when I do php -i I see that the Brew version is running, because the build date was yesterday. Can it be that the command line Curl is the old one and not the new one I brewed yesterday? – Mattijs Oct 30 '14 at 00:59
  • Another interesting thing is that my calls to Canvas API ( `curl -v -H "Accept: application/json" -H "Content-type: application/json" -X GET -d '{"userid": "mohit", "password":"password"}' https://matrix.instructure.com/api/v1/accounts` work fine in terminal Curl, but through PHP I am getting `SSL certificate problem: unable to get local issuer certificate`. So my original problem is fixed in php, but doesnt work in curl. And my other call Canvas API that used to work in PHP doesn't any more, but works in terminal :) – Mattijs Oct 30 '14 at 01:05
  • I did try to add a PEM file to my php.ini, `curl.cainfo = "/usr/local/cacert.pem` but that triggered another error `error setting certificate verify locations: CAfile: /usr/local/cacert.pem CApath: none`. I am a bit puzzled. I need to have the Brew PHP Curl version working for both API's. Now one who wasnt working is working, but the other one which was working isnt. (throwing the unable to get local issuer certiciface message). Any wisdom would be appreciated. – Mattijs Oct 30 '14 at 01:37
  • @Mattijs: Your command line `curl` is the stock Apple curl (`/usr/bin/curl`) which uses SecureTransport and suffers from the SSLRead() error. This is different than the brewed curl (`/usr/local/Cellar/curl/7.38.0/bin/curl`) used by brewed php. When you `brew install curl`, it doesn't perform the link step. Instead it tells you: "Mac OS X already provides this software and installing another version in parallel can cause all kinds of trouble." You can still do terminal tests with brewed curl by using the full path. e.g.: `/usr/local/Cellar/curl/7.38.0/bin/curl http://www.example.com/`. – Asaph Oct 30 '14 at 04:28
  • @Mattijs As for your Canvas API certificate issue, please post a new question to resolve that one. The comments on this question are getting too cluttered. You can link to this question in your new one for context. – Asaph Oct 30 '14 at 04:30
  • Thanks! I created a new one: http://stackoverflow.com/questions/26666920/osx-home-brew-php-5-5-with-curl-triggers-ssl-unable-to-get-local-issuer-certifi – Mattijs Oct 31 '14 at 03:00
  • 1
    I had to follow the `brew tap` instructions at [(Re)installing PHP on Mac OS X](http://justinhileman.info/article/reinstalling-php-on-mac-os-x/) before I could run `brew install php55`, but other than that this answer was solid. –  Jan 27 '15 at 01:17
  • I have been using Yosemite's version of PHP, will this stomp on all the virtual hosts and things that I have set up in the Apache configuration? – Trevor Hutto Feb 19 '15 at 00:06
  • @TrevorHutto Virtual hosts will not be affected but you will have to modify your `/etc/apache2/httpd.conf` to reference homebrew's `libphp5.so` instead of Apple's as described in the answer. You'll have to restart Apache too. If you change your mind and want to restore Yosemite's php in your Apache config, simply undo the change in `/etc/apache2/httpd.conf`, `brew rm` any php packages you've installed and restart Apache again. – Asaph Feb 19 '15 at 00:19
  • Is there an alternative besides installing another version of PHP? – Tony Feb 26 '15 at 06:57
  • 1
    @Tony You can recompile Apple's version of PHP but that seems more invasive. What's nice about homebrew is that it installs everything into `/usr/local` (which OSX doesn't touch) and has it "override" binaries in `/usr`. So if one day you decide you want to go back to using Apple's version of PHP, you simply remove the one installed by homebrew and your system goes back to the way it was before. – Asaph Feb 26 '15 at 07:04
  • My colleagues advised us not to install both macports and homebrew, and we use macports, thats why I asked for an alternative. But anyway, your comment sounds right, I may just ignore them :) – Tony Feb 26 '15 at 11:18
  • I tried to get this working but I get the following error about the wrong version of curl: httpd: Syntax error on line 171 of /private/etc/apache2/httpd.conf: Cannot load /usr/local/opt/php55/libexec/apache2/libphp5.so into server: dlopen(/usr/local/opt/php55/libexec/apache2/libphp5.so, 10): Library not loaded: /usr/local/opt/curl/lib/libcurl.4.dylib\n Referenced from: /usr/local/opt/php55/libexec/apache2/libphp5.so\n Reason: Incompatible library version: libphp5.so requires version 8.0.0 or later, but libcurl.4.dylib provides version 7.0.0 – jtotheh Jul 07 '15 at 13:07
  • I'm using OS X's native `curl` on OS X 10.11 El Capitan. `curl --version` results in `curl 7.41.0 (x86_64-apple-darwin14.0.0) libcurl/7.41.0 OpenSSL/1.0.2 zlib/1.2.8 libidn/1.29 Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP UnixSockets` and using a `POST` to an `https` server from the Terminal also results in the `SSLRead() return error -9806` error. This command must work on Macs & servers I don't administer. Is there anything that can be done? – Jon Oct 09 '15 at 15:16
  • @Jon Are you able to switch to brewed cURL? When you install cURL via homebrew, it remains unlinked so that it doesn't interfere with OSX internals. But you can still use it on the command line by specifying the full path. eg. `/usr/local/Cellar/curl//bin/curl --version`. – Asaph Oct 09 '15 at 18:38
  • @Asaph Thanks for the suggestion. I can't install in `/usr` but I might be able to include a version of `curl` in my app's bundle and use that but it'd be preferred if I could just use OS X's version. – Jon Oct 09 '15 at 20:35
  • Followed this a few times and still doesn't work. Please help – RyanCameron.Me Apr 11 '17 at 18:42
  • @RyanCameron.Me Please post a new question and reference this one in it. Make sure you're specific about what "doesn't work" means. – Asaph Apr 12 '17 at 03:26
  • I followed these steps and it still says "SSL Version => SecureTransport". When I tried the "brew tap homebrew/dupes" command it gave me this warning "homebrew/dupes was deprecated. This tap is now empty as all its formulae were migrated." Please help, my migrate db pro won't work :( – RyanCameron.Me Aug 03 '17 at 17:49
  • It seems like the php tap doesn't exist anymore and moved to hombrebrew-core [post here](https://brew.sh/2018/01/19/homebrew-1.5.0/), I didn't exhaustively test it, but it seems like the https connections now work out of the box... can anyone confirm this? I just installed php@7.1 – Javier P Apr 04 '18 at 23:03
  • also I forgot to point out that the argument `--with-homebrew-curl` no longer exists, but `--with-curl`. Don't know if the result is the same, it seems like it doesn't compile binaries like before – Javier P Apr 04 '18 at 23:11
  • This solution does not work anymore? Did someone manage the get this to work recently? – Alfonso Fernandez-Ocampo Jul 05 '18 at 12:59
5

This SSL error (OSStatus code: 9806) means that your connection is terminated by the server due to an error in establishing the connection (e.g. on some invalid command). This seems to happens only on occasions when the SSL connection to remote host drops in between.

This is not well documented by SSL manual (SSL_get_error), however this error message comes from libcurl built which is used by the SecureTransport/Darwinssl TLS backend (you can find its OSStatus in SecureTransport.h header file):

errSSLClosedAbort           = -9806,    /* connection closed via error */

From my experience, this usually happens when you're behind the proxy or connected to a limited network which uses authentication mechanism.

So please verify that you're connected to the right network (via WiFi) and your other HTTPS works correctly. If not, check if you need to specify proxy credentials or your ISP is overriding the certificate chain and requires some kind of authentication or it's basically blocking access to certain sites in their firewall.

kenorb
  • 155,785
  • 88
  • 678
  • 743
1

I had a similar issue with SSLRead() return error -9806 error, and also I had SSL Version => SecureTransport.

But in my case the problem was that I was setting curl CURLOPT_HTTP_VERSION option:

$curl = curl_init();    
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);

If you remove that option, cURL will decide which version to use by default. Check curl_setopt documentation for more.

That worked for me, and I didn't need to change anything with cURL nor PHP. But this is a solution of one of many cases where error -9806 appears.

Daniel Batalla
  • 1,184
  • 1
  • 11
  • 22