@@ -30,35 +30,151 @@ trait HTTPOptionsTrait{
3030 protected $ curl_options = [];
3131
3232 /**
33- * CA Root Certificates for use with CURL/SSL (if not configured in php.ini)
33+ * CA Root Certificates for use with CURL/SSL (if not configured in php.ini or available in a default path )
3434 *
3535 * @var string
36+ *
37+ * @link https://curl.haxx.se/docs/caextract.html
3638 * @link https://curl.haxx.se/ca/cacert.pem
3739 * @link https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt
3840 */
3941 protected $ ca_info = null ;
4042
43+ /**
44+ * see CURLOPT_SSL_VERIFYPEER
45+ * requires either HTTPOptions::$ca_info or a properly working system CA file
46+ *
47+ * @var bool
48+ * @link http://php.net/manual/en/function.curl-setopt.php
49+ */
50+ protected $ ssl_verifypeer = true ;
51+
4152 /**
4253 * HTTPOptionsTrait constructor
4354 *
44- * @throws \chillerlan\HTTP\ClientException
55+ * @throws \Psr\Http\Client\ClientExceptionInterface
4556 */
4657 protected function HTTPOptionsTrait ():void {
4758
4859 if (!is_array ($ this ->curl_options )){
4960 $ this ->curl_options = [];
5061 }
5162
52- // we cannot verify a peer against a non-existent ca file, so turn it off in that case
53- /* if(!$this->ca_info || !is_file($this->ca_info)
54- || (isset($this->curl_options[CURLOPT_CAINFO]) && !is_file($this->curl_options[CURLOPT_CAINFO]))){
63+ if (!is_string ($ this ->user_agent ) || empty (trim ($ this ->user_agent ))){
64+ throw new ClientException ('invalid user agent ' );
65+ }
66+
67+ $ this ->setCA ();
68+ }
69+
70+ /**
71+ * @return void
72+ * @throws \Psr\Http\Client\ClientExceptionInterface
73+ */
74+ protected function setCA ():void {
75+
76+ // disable verification if wanted so
77+ if ($ this ->ssl_verifypeer !== true || (isset ($ this ->curl_options [CURLOPT_SSL_VERIFYPEER ]) && !$ this ->curl_options [CURLOPT_SSL_VERIFYPEER ])){
78+ unset($ this ->curl_options [CURLOPT_CAINFO ], $ this ->curl_options [CURLOPT_CAPATH ]);
79+
80+ $ this ->curl_options [CURLOPT_SSL_VERIFYHOST ] = 0 ;
81+ $ this ->curl_options [CURLOPT_SSL_VERIFYPEER ] = false ;
82+
83+ return ;
84+ }
85+
86+ $ this ->curl_options [CURLOPT_SSL_VERIFYHOST ] = 2 ;
87+ $ this ->curl_options [CURLOPT_SSL_VERIFYPEER ] = true ;
88+
89+ // a path/dir/link to a CA bundle is given, let's check that
90+ if (is_string ($ this ->ca_info )){
91+
92+ // if you - for whatever obscure reason - need to check Windows .lnk links,
93+ // see http://php.net/manual/en/function.is-link.php#91249
94+ switch (true ){
95+ case is_dir ($ this ->ca_info ):
96+ case is_link ($ this ->ca_info ) && is_dir (readlink ($ this ->ca_info )): // @codeCoverageIgnore
97+ $ this ->curl_options [CURLOPT_CAPATH ] = $ this ->ca_info ;
98+ unset($ this ->curl_options [CURLOPT_CAINFO ]);
99+ return ;
100+
101+ case is_file ($ this ->ca_info ):
102+ case is_link ($ this ->ca_info ) && is_file (readlink ($ this ->ca_info )): // @codeCoverageIgnore
103+ $ this ->curl_options [CURLOPT_CAINFO ] = $ this ->ca_info ;
104+ unset($ this ->curl_options [CURLOPT_CAPATH ]);
105+ return ;
106+ }
107+
108+ throw new ClientException ('invalid path to SSL CA bundle (HTTPOptions::$ca_info): ' .$ this ->ca_info );
109+ }
110+
111+ // we somehow landed here, so let's check if there's a CA bundle given via the cURL options
112+ $ ca = $ this ->curl_options [CURLOPT_CAPATH ] ?? $ this ->curl_options [CURLOPT_CAINFO ] ?? false ;
113+
114+ if ($ ca ){
115+
116+ // just check if the file/path exists
117+ switch (true ){
118+ case is_dir ($ ca ):
119+ case is_link ($ ca ) && is_dir (readlink ($ ca )): // @codeCoverageIgnore
120+ unset($ this ->curl_options [CURLOPT_CAINFO ]);
121+ return ;
122+
123+ case is_file ($ ca ):
124+ case is_link ($ ca ) && is_file (readlink ($ ca )): // @codeCoverageIgnore
125+ return ;
126+ }
127+
128+ throw new ClientException ('invalid path to SSL CA bundle (CURLOPT_CAPATH/CURLOPT_CAINFO): ' .$ ca );
129+ }
130+
131+ // check php.ini options - PHP should find the file by itself
132+ if (file_exists (ini_get ('curl.cainfo ' ))){
133+ return ; // @codeCoverageIgnore
134+ }
135+
136+ // this is getting weird. as a last resort, we're going to check some default paths for a CA bundle file
137+ $ cafiles = [
138+ // check other php.ini settings
139+ ini_get ('openssl.cafile ' ),
140+ // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
141+ '/etc/pki/tls/certs/ca-bundle.crt ' ,
142+ // Ubuntu, Debian (provided by the ca-certificates package)
143+ '/etc/ssl/certs/ca-certificates.crt ' ,
144+ // FreeBSD (provided by the ca_root_nss package)
145+ '/usr/local/share/certs/ca-root-nss.crt ' ,
146+ // SLES 12 (provided by the ca-certificates package)
147+ '/var/lib/ca-certificates/ca-bundle.pem ' ,
148+ // OS X provided by homebrew (using the default path)
149+ '/usr/local/etc/openssl/cert.pem ' ,
150+ // Google app engine
151+ '/etc/ca-certificates.crt ' ,
152+ // Windows?
153+ // http://php.net/manual/en/function.curl-setopt.php#110457
154+ 'C: \\Windows \\system32 \\curl-ca-bundle.crt ' ,
155+ 'C: \\Windows \\curl-ca-bundle.crt ' ,
156+ 'C: \\Windows \\system32 \\cacert.pem ' ,
157+ 'C: \\Windows \\cacert.pem ' ,
158+ // working path
159+ __DIR__ .'/cacert.pem ' ,
160+ ];
161+
162+ foreach ($ cafiles as $ file ){
163+ if (is_file ($ file ) || (is_link ($ file ) && is_file (readlink ($ file )))){
164+ $ this ->curl_options [CURLOPT_CAINFO ] = $ file ;
165+ return ;
166+ }
167+ }
55168
56- $this->curl_options += [
57- CURLOPT_SSL_VERIFYPEER => false,
58- CURLOPT_CAINFO => null,
59- ];
60- }*/
169+ $ msg = 'No system CA bundle could be found in any of the the common system locations. '
170+ .'In order to verify peer certificates, you will need to supply the path on disk to a certificate bundle via '
171+ .'HTTPOptions::$ca_info or HTTPOptions::$curl_options. If you do not need a specific certificate bundle, '
172+ .'then you can download a CA bundle over here: https://curl.haxx.se/docs/caextract.html. '
173+ .'Once you have a CA bundle available on disk, you can set the "curl.cainfo" php.ini setting to point '
174+ .'to the path to the file, allowing you to omit the $ca_info or $curl_options setting. '
175+ .'See http://curl.haxx.se/docs/sslcerts.html for more information. ' ;
61176
177+ throw new ClientException ($ msg ); // @codeCoverageIgnore
62178 }
63179
64180}
0 commit comments