[ 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/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package) 85 '/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package) 86 '/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package) 87 '/usr/ssl/certs/ca-bundle.crt', // Cygwin 88 '/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package 89 '/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option) 90 '/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat? 91 '/etc/ssl/cert.pem', // OpenBSD 92 '/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package 93 '/usr/local/etc/[email protected]/cert.pem', // OS X homebrew, [email protected] package 94 '/opt/homebrew/etc/openssl@3/cert.pem', // macOS silicon homebrew, openssl@3 package 95 '/opt/homebrew/etc/[email protected]/cert.pem', // macOS silicon homebrew, [email protected] package 96 '/etc/pki/tls/certs', 97 '/etc/ssl/certs', // FreeBSD 98 ); 99 100 $caBundlePaths = array_merge($caBundlePaths, $otherLocations); 101 102 foreach ($caBundlePaths as $caBundle) { 103 if ($caBundle && self::caFileUsable($caBundle, $logger)) { 104 return self::$caPath = $caBundle; 105 } 106 107 if ($caBundle && self::caDirUsable($caBundle, $logger)) { 108 return self::$caPath = $caBundle; 109 } 110 } 111 112 return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort 113 } 114 115 /** 116 * Returns the path to the bundled CA file 117 * 118 * In case you don't want to trust the user or the system, you can use this directly 119 * 120 * @return string path to a CA bundle file 121 */ 122 public static function getBundledCaBundlePath() 123 { 124 $caBundleFile = __DIR__.'/../res/cacert.pem'; 125 126 // cURL does not understand 'phar://' paths 127 // see https://github.com/composer/ca-bundle/issues/10 128 if (0 === strpos($caBundleFile, 'phar://')) { 129 $tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-'); 130 if (false === $tempCaBundleFile) { 131 throw new \RuntimeException('Could not create a temporary file to store the bundled CA file'); 132 } 133 134 file_put_contents( 135 $tempCaBundleFile, 136 file_get_contents($caBundleFile) 137 ); 138 139 register_shutdown_function(function() use ($tempCaBundleFile) { 140 @unlink($tempCaBundleFile); 141 }); 142 143 $caBundleFile = $tempCaBundleFile; 144 } 145 146 return $caBundleFile; 147 } 148 149 /** 150 * Validates a CA file using opensl_x509_parse only if it is safe to use 151 * 152 * @param string $filename 153 * @param LoggerInterface $logger optional logger for information about which CA files were loaded 154 * 155 * @return bool 156 */ 157 public static function validateCaFile($filename, ?LoggerInterface $logger = null) 158 { 159 static $warned = false; 160 161 if (isset(self::$caFileValidity[$filename])) { 162 return self::$caFileValidity[$filename]; 163 } 164 165 $contents = file_get_contents($filename); 166 167 if (is_string($contents) && strlen($contents) > 0) { 168 $contents = preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents); 169 if (null === $contents) { 170 // regex extraction failed 171 $isValid = false; 172 } else { 173 $isValid = (bool) openssl_x509_parse($contents); 174 } 175 } else { 176 $isValid = false; 177 } 178 179 if ($logger) { 180 $logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid')); 181 } 182 183 return self::$caFileValidity[$filename] = $isValid; 184 } 185 186 /** 187 * Test if it is safe to use the PHP function openssl_x509_parse(). 188 * 189 * This checks if OpenSSL extensions is vulnerable to remote code execution 190 * via the exploit documented as CVE-2013-6420. 191 * 192 * @return bool 193 */ 194 public static function isOpensslParseSafe() 195 { 196 return true; 197 } 198 199 /** 200 * Resets the static caches 201 * @return void 202 */ 203 public static function reset() 204 { 205 self::$caFileValidity = array(); 206 self::$caPath = null; 207 } 208 209 /** 210 * @param string $name 211 * @return string|false 212 */ 213 private static function getEnvVariable($name) 214 { 215 if (isset($_SERVER[$name])) { 216 return (string) $_SERVER[$name]; 217 } 218 219 if (PHP_SAPI === 'cli' && ($value = getenv($name)) !== false && $value !== null) { 220 return (string) $value; 221 } 222 223 return false; 224 } 225 226 /** 227 * @param string|false $certFile 228 * @param LoggerInterface|null $logger 229 * @return bool 230 */ 231 private static function caFileUsable($certFile, ?LoggerInterface $logger = null) 232 { 233 return $certFile 234 && self::isFile($certFile, $logger) 235 && self::isReadable($certFile, $logger) 236 && self::validateCaFile($certFile, $logger); 237 } 238 239 /** 240 * @param string|false $certDir 241 * @param LoggerInterface|null $logger 242 * @return bool 243 */ 244 private static function caDirUsable($certDir, ?LoggerInterface $logger = null) 245 { 246 return $certDir 247 && self::isDir($certDir, $logger) 248 && self::isReadable($certDir, $logger) 249 && self::glob($certDir . '/*', $logger); 250 } 251 252 /** 253 * @param string $certFile 254 * @param LoggerInterface|null $logger 255 * @return bool 256 */ 257 private static function isFile($certFile, ?LoggerInterface $logger = null) 258 { 259 $isFile = @is_file($certFile); 260 if (!$isFile && $logger) { 261 $logger->debug(sprintf('Checked CA file %s does not exist or it is not a file.', $certFile)); 262 } 263 264 return $isFile; 265 } 266 267 /** 268 * @param string $certDir 269 * @param LoggerInterface|null $logger 270 * @return bool 271 */ 272 private static function isDir($certDir, ?LoggerInterface $logger = null) 273 { 274 $isDir = @is_dir($certDir); 275 if (!$isDir && $logger) { 276 $logger->debug(sprintf('Checked directory %s does not exist or it is not a directory.', $certDir)); 277 } 278 279 return $isDir; 280 } 281 282 /** 283 * @param string $certFileOrDir 284 * @param LoggerInterface|null $logger 285 * @return bool 286 */ 287 private static function isReadable($certFileOrDir, ?LoggerInterface $logger = null) 288 { 289 $isReadable = @is_readable($certFileOrDir); 290 if (!$isReadable && $logger) { 291 $logger->debug(sprintf('Checked file or directory %s is not readable.', $certFileOrDir)); 292 } 293 294 return $isReadable; 295 } 296 297 /** 298 * @param string $pattern 299 * @param LoggerInterface|null $logger 300 * @return bool 301 */ 302 private static function glob($pattern, ?LoggerInterface $logger = null) 303 { 304 $certs = glob($pattern); 305 if ($certs === false) { 306 if ($logger) { 307 $logger->debug(sprintf("An error occurred while trying to find certificates for pattern: %s", $pattern)); 308 } 309 return false; 310 } 311 312 if (count($certs) === 0) { 313 if ($logger) { 314 $logger->debug(sprintf("No CA files found for pattern: %s", $pattern)); 315 } 316 return false; 317 } 318 319 return true; 320 } 321 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Mar 31 05:10:02 2025 | Cross-referenced by PHPXref 0.7.1 |