| [ Index ] |
PHP Cross Reference of YOURLS |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of composer/ca-bundle. 5 * 6 * (c) Composer <https://github.com/composer> 7 * 8 * For the full copyright and license information, please view 9 * the LICENSE file that was distributed with this source code. 10 */ 11 12 namespace Composer\CaBundle; 13 14 use Psr\Log\LoggerInterface; 15 use Symfony\Component\Process\PhpProcess; 16 17 /** 18 * @author Chris Smith <[email protected]> 19 * @author Jordi Boggiano <[email protected]> 20 */ 21 class CaBundle 22 { 23 /** @var string|null */ 24 private static $caPath; 25 /** @var array<string, bool> */ 26 private static $caFileValidity = array(); 27 28 /** 29 * Returns the system CA bundle path, or a path to the bundled one 30 * 31 * This method was adapted from Sslurp. 32 * https://github.com/EvanDotPro/Sslurp 33 * 34 * (c) Evan Coury <[email protected]> 35 * 36 * For the full copyright and license information, please see below: 37 * 38 * Copyright (c) 2013, Evan Coury 39 * All rights reserved. 40 * 41 * Redistribution and use in source and binary forms, with or without modification, 42 * are permitted provided that the following conditions are met: 43 * 44 * * Redistributions of source code must retain the above copyright notice, 45 * this list of conditions and the following disclaimer. 46 * 47 * * Redistributions in binary form must reproduce the above copyright notice, 48 * this list of conditions and the following disclaimer in the documentation 49 * and/or other materials provided with the distribution. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 53 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 54 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 55 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 56 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 57 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 58 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 * 62 * @param LoggerInterface $logger optional logger for information about which CA files were loaded 63 * @return string path to a CA bundle file or directory 64 */ 65 public static function getSystemCaRootBundlePath(?LoggerInterface $logger = null) 66 { 67 if (self::$caPath !== null) { 68 return self::$caPath; 69 } 70 $caBundlePaths = array(); 71 72 // If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that. 73 // This mimics how OpenSSL uses the SSL_CERT_FILE env variable. 74 $caBundlePaths[] = self::getEnvVariable('SSL_CERT_FILE'); 75 76 // If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that. 77 // This mimics how OpenSSL uses the SSL_CERT_FILE env variable. 78 $caBundlePaths[] = self::getEnvVariable('SSL_CERT_DIR'); 79 80 $caBundlePaths[] = ini_get('openssl.cafile'); 81 $caBundlePaths[] = ini_get('openssl.capath'); 82 83 $otherLocations = array( 84 '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem', // Fedora, RHEL, CentOS (ca-certificates package) - NEW 85 '/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package) - Deprecated 86 '/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package) 87 '/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package) 88 '/usr/ssl/certs/ca-bundle.crt', // Cygwin 89 '/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package 90 '/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option) 91 '/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat? 92 '/etc/ssl/cert.pem', // OpenBSD 93 '/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package 94 '/usr/local/etc/[email protected]/cert.pem', // OS X homebrew, [email protected] package 95 '/opt/homebrew/etc/openssl@3/cert.pem', // macOS silicon homebrew, openssl@3 package 96 '/opt/homebrew/etc/[email protected]/cert.pem', // macOS silicon homebrew, [email protected] package 97 '/etc/pki/tls/certs', 98 '/etc/ssl/certs', // FreeBSD 99 ); 100 101 $caBundlePaths = array_merge($caBundlePaths, $otherLocations); 102 103 foreach ($caBundlePaths as $caBundle) { 104 if ($caBundle && self::caFileUsable($caBundle, $logger)) { 105 return self::$caPath = $caBundle; 106 } 107 108 if ($caBundle && self::caDirUsable($caBundle, $logger)) { 109 return self::$caPath = $caBundle; 110 } 111 } 112 113 return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort 114 } 115 116 /** 117 * Returns the path to the bundled CA file 118 * 119 * In case you don't want to trust the user or the system, you can use this directly 120 * 121 * @return string path to a CA bundle file 122 */ 123 public static function getBundledCaBundlePath() 124 { 125 $caBundleFile = __DIR__.'/../res/cacert.pem'; 126 127 // cURL does not understand 'phar://' paths 128 // see https://github.com/composer/ca-bundle/issues/10 129 if (0 === strpos($caBundleFile, 'phar://')) { 130 $tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-'); 131 if (false === $tempCaBundleFile) { 132 throw new \RuntimeException('Could not create a temporary file to store the bundled CA file'); 133 } 134 135 file_put_contents( 136 $tempCaBundleFile, 137 file_get_contents($caBundleFile) 138 ); 139 140 register_shutdown_function(function() use ($tempCaBundleFile) { 141 @unlink($tempCaBundleFile); 142 }); 143 144 $caBundleFile = $tempCaBundleFile; 145 } 146 147 return $caBundleFile; 148 } 149 150 /** 151 * Validates a CA file using opensl_x509_parse only if it is safe to use 152 * 153 * @param string $filename 154 * @param LoggerInterface $logger optional logger for information about which CA files were loaded 155 * 156 * @return bool 157 */ 158 public static function validateCaFile($filename, ?LoggerInterface $logger = null) 159 { 160 static $warned = false; 161 162 if (isset(self::$caFileValidity[$filename])) { 163 return self::$caFileValidity[$filename]; 164 } 165 166 $contents = file_get_contents($filename); 167 168 if (is_string($contents) && strlen($contents) > 0) { 169 $contents = preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents); 170 if (null === $contents) { 171 // regex extraction failed 172 $isValid = false; 173 } else { 174 $isValid = (bool) openssl_x509_parse($contents); 175 } 176 } else { 177 $isValid = false; 178 } 179 180 if ($logger) { 181 $logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid')); 182 } 183 184 return self::$caFileValidity[$filename] = $isValid; 185 } 186 187 /** 188 * Test if it is safe to use the PHP function openssl_x509_parse(). 189 * 190 * This checks if OpenSSL extensions is vulnerable to remote code execution 191 * via the exploit documented as CVE-2013-6420. 192 * 193 * @return bool 194 */ 195 public static function isOpensslParseSafe() 196 { 197 return true; 198 } 199 200 /** 201 * Resets the static caches 202 * @return void 203 */ 204 public static function reset() 205 { 206 self::$caFileValidity = array(); 207 self::$caPath = null; 208 } 209 210 /** 211 * @param string $name 212 * @return string|false 213 */ 214 private static function getEnvVariable($name) 215 { 216 if (isset($_SERVER[$name])) { 217 return (string) $_SERVER[$name]; 218 } 219 220 if (PHP_SAPI === 'cli' && ($value = getenv($name)) !== false && $value !== null) { 221 return (string) $value; 222 } 223 224 return false; 225 } 226 227 /** 228 * @param string|false $certFile 229 * @param LoggerInterface|null $logger 230 * @return bool 231 */ 232 private static function caFileUsable($certFile, ?LoggerInterface $logger = null) 233 { 234 return $certFile 235 && self::isFile($certFile, $logger) 236 && self::isReadable($certFile, $logger) 237 && self::validateCaFile($certFile, $logger); 238 } 239 240 /** 241 * @param string|false $certDir 242 * @param LoggerInterface|null $logger 243 * @return bool 244 */ 245 private static function caDirUsable($certDir, ?LoggerInterface $logger = null) 246 { 247 return $certDir 248 && self::isDir($certDir, $logger) 249 && self::isReadable($certDir, $logger) 250 && self::glob($certDir . '/*', $logger); 251 } 252 253 /** 254 * @param string $certFile 255 * @param LoggerInterface|null $logger 256 * @return bool 257 */ 258 private static function isFile($certFile, ?LoggerInterface $logger = null) 259 { 260 $isFile = @is_file($certFile); 261 if (!$isFile && $logger) { 262 $logger->debug(sprintf('Checked CA file %s does not exist or it is not a file.', $certFile)); 263 } 264 265 return $isFile; 266 } 267 268 /** 269 * @param string $certDir 270 * @param LoggerInterface|null $logger 271 * @return bool 272 */ 273 private static function isDir($certDir, ?LoggerInterface $logger = null) 274 { 275 $isDir = @is_dir($certDir); 276 if (!$isDir && $logger) { 277 $logger->debug(sprintf('Checked directory %s does not exist or it is not a directory.', $certDir)); 278 } 279 280 return $isDir; 281 } 282 283 /** 284 * @param string $certFileOrDir 285 * @param LoggerInterface|null $logger 286 * @return bool 287 */ 288 private static function isReadable($certFileOrDir, ?LoggerInterface $logger = null) 289 { 290 $isReadable = @is_readable($certFileOrDir); 291 if (!$isReadable && $logger) { 292 $logger->debug(sprintf('Checked file or directory %s is not readable.', $certFileOrDir)); 293 } 294 295 return $isReadable; 296 } 297 298 /** 299 * @param string $pattern 300 * @param LoggerInterface|null $logger 301 * @return bool 302 */ 303 private static function glob($pattern, ?LoggerInterface $logger = null) 304 { 305 $certs = glob($pattern); 306 if ($certs === false) { 307 if ($logger) { 308 $logger->debug(sprintf("An error occurred while trying to find certificates for pattern: %s", $pattern)); 309 } 310 return false; 311 } 312 313 if (count($certs) === 0) { 314 if ($logger) { 315 $logger->debug(sprintf("No CA files found for pattern: %s", $pattern)); 316 } 317 return false; 318 } 319 320 return true; 321 } 322 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Sat Jul 11 05:10:54 2026 | Cross-referenced by PHPXref 0.7.1 |