Test if it is safe to use the PHP function openssl_x509_parse().

This checks if OpenSSL extensions is vulnerable to remote code execution
via the exploit documented as CVE-2013-6420.

@return bool
486201
public static function isOpensslParseSafe()
    {
        if (null !== self::$useOpensslParse) {
            return self::$useOpensslParse;
        }

        if (PHP_VERSION_ID >= 50600) {
            return self::$useOpensslParse = true;
        }

        // Vulnerable:
        // PHP 5.3.0 - PHP 5.3.27
        // PHP 5.4.0 - PHP 5.4.22
        // PHP 5.5.0 - PHP 5.5.6
        if (
               (PHP_VERSION_ID < 50400 && PHP_VERSION_ID >= 50328)
            || (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50423)
            || (PHP_VERSION_ID < 50600 && PHP_VERSION_ID >= 50507)
        ) {
            // This version of PHP has the fix for CVE-2013-6420 applied.
            return self::$useOpensslParse = true;
        }

        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
            // Windows is probably insecure in this case.
            return self::$useOpensslParse = false;
        }

        $compareDistroVersionPrefix = function ($prefix, $fixedVersion) {
            $regex = '{^'.preg_quote($prefix).'([0-9]+)$}';

            if (preg_match($regex, PHP_VERSION, $m)) {
                return ((int) $m[1]) >= $fixedVersion;
            }

            return false;
        };

        // Hard coded list of PHP distributions with the fix backported.
        if (
            $compareDistroVersionPrefix('5.3.3-7+squeeze', 18) // Debian 6 (Squeeze)
            || $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) // Debian 7 (Wheezy)
            || $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9) // Ubuntu 12.04 (Precise)
        ) {
            return self::$useOpensslParse = true;
        }

        // Symfony Process component is missing so we assume it is unsafe at this point
        if (!class_exists('Symfony\Component\Process\PhpProcess')) {
            return self::$useOpensslParse = false;
        }

        // This is where things get crazy, because distros backport security
        // fixes the chances are on NIX systems the fix has been applied but
        // it's not possible to verify that from the PHP version.
        //
        // To verify exec a new PHP process and run the issue testcase with
        // known safe input that replicates the bug.

        // Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415
        // changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593
        $cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K';
        $script = <<<'EOT'

error_reporting(-1);
$info = openssl_x509_parse(base64_decode('%s'));
var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']);

EOT;
        $script = '<'."?php\n".sprintf($script, $cert);

        try {
            $process = new PhpProcess($script);
            $process->mustRun();
        } catch (\Exception $e) {
            // In the case of any exceptions just accept it is not possible to
            // determine the safety of openssl_x509_parse and bail out.
            return self::$useOpensslParse = false;
        }

        $output = preg_split('{\r?\n}', trim($process->getOutput()));
        $errorOutput = trim($process->getErrorOutput());

        if (
            count($output) === 3
            && $output[0] === sprintf('string(%d) "%s"', strlen(PHP_VERSION), PHP_VERSION)
            && $output[1] === 'string(27) "stefan.esser@sektioneins.de"'
            && $output[2] === 'int(-1)'
            && preg_match('{openssl_x509_parse\(\): illegal (?:ASN1 data type for|length in) timestamp in - on line \d+}', $errorOutput)
        ) {
            // This PHP has the fix backported probably by a distro security team.
            return self::$useOpensslParse = true;
        }

        return self::$useOpensslParse = false;
    }
625444
function loadX509($cert)
    {
        if (is_array($cert) && isset($cert['tbsCertificate'])) {
            $this->currentCert = $cert;
            unset($this->signatureSubject);
            return false;
        }

        $asn1 = new File_ASN1();

        /*
            X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them above and beyond the ceritificate. ie.
            some may have the following preceeding the -----BEGIN CERTIFICATE----- line:

            subject=/O=organization/OU=org unit/CN=common name
            issuer=/O=organization/CN=common name
        */
        $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $cert);
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
        if ($temp != false) {
            $cert = $temp;
        }

        if ($cert === false) {
            $this->currentCert = false;
            return false;
        }

        $asn1->loadOIDs($this->oids);
        $decoded = $asn1->decodeBER($cert);

        if (!empty($decoded)) {
            $x509 = $asn1->asn1map($decoded[0], $this->Certificate);
        }
        if (!isset($x509) || $x509 === false) {
            $this->currentCert = false;
            return false;
        }

        $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1);

        $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
        $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);

        $this->currentCert = $x509;
        $this->dn = $x509['tbsCertificate']['subject'];

        $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier');
        $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : NULL;

        return $x509;
    }
939309
function supportsSSL()
    {
        $v = curl_version();
        if(is_array($v)) {
            return in_array('https', $v['protocols']);
        } elseif (is_string($v)) {
            return preg_match('/OpenSSL/i', $v);
        } else {
            return 0;
        }
    }
344282
public function is_valid()
    {
        $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null;
        if ($this->ipath !== '' &&
            (
                $isauthority && (
                    $this->ipath[0] !== '/' ||
                    substr($this->ipath, 0, 2) === '//'
                ) ||
                (
                    $this->scheme === null &&
                    !$isauthority &&
                    strpos($this->ipath, ':') !== false &&
                    (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/'))
                )
            )
        )
        {
            return false;
        }

        return true;
    }
1158162
private static function ensureKeyPair()
    {
        $publicKeyLocation = DIR_DATA . 'xentriq/security/xentriq.security.website.pub';
        $privateKeyLocation = DIR_DATA . 'xentriq/security/xentriq.security.website.priv';
        
        if (!file_exists($publicKeyLocation) || !file_exists($privateKeyLocation)) {
            $privateKey = openssl_pkey_new(array(
                'private_key_bits' => 2048
            ));
            
            File::setContent($privateKeyLocation, '', true);
            if (!openssl_pkey_export_to_file($privateKey, $privateKeyLocation)) {
                throw new SecurityException('Unable to generate security token');
            }
            
            $privateKeyDetails = openssl_pkey_get_details($privateKey);
            File::setContent($publicKeyLocation, $privateKeyDetails['key'], true);
        }
    }
1201481
public function valid()
    {
        $encryptStr = "";
        if ($_SERVER['REQUEST_METHOD'] == "POST") {
            $postStr = file_get_contents("php://input");
            $disableEntities = libxml_disable_entity_loader(true);
            $array = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            libxml_disable_entity_loader($disableEntities);
            $this->encrypt_type = isset($_GET["encrypt_type"]) ? $_GET["encrypt_type"] : '';
            if ($this->encrypt_type == 'aes') {
                $encryptStr = $array['Encrypt'];
                !class_exists('Prpcrypt', false) && require __DIR__ . '/Prpcrypt.php';
                $pc = new Prpcrypt($this->encodingAesKey);
                $array = $pc->decrypt($encryptStr, $this->appid);
                if (!isset($array[0]) || intval($array[0]) > 0) {
                    $this->errCode = $array[0];
                    $this->errMsg = $array[1];
                    Tools::log("Interface Authentication Failed. {$this->errMsg}[{$this->errCode}]", "ERR - {$this->appid}");
                    return false;
                }
                $this->postxml = $array[1];
                empty($this->appid) && $this->appid = $array[2];
            } else {
                $this->postxml = $postStr;
            }
        } elseif (isset($_GET["echostr"])) {
            if ($this->checkSignature()) {
                @ob_clean();
                exit($_GET["echostr"]);
            }
            return false;
        }
        if (!$this->checkSignature($encryptStr)) {
            $this->errMsg = 'Interface authentication failed, please use the correct method to call.';
            return false;
        }
        return true;
    }
147123
private static function useOpenSslBacking()
    {
        if (!extension_loaded(self::BACKING_OPENSSL)) {
            throw new \RuntimeException(
                    'Can not use openssl backing, extension openssl not available'
            );
        }

        $key = md5(uniqid());
        $iv  = substr(md5(uniqid()), 0, openssl_cipher_iv_length('des'));
        self::$encrypt = function($value) use ($key, $iv) { return openssl_encrypt($value, 'DES', $key,  0, $iv); };
        self::$decrypt = function($value) use ($key, $iv) { return openssl_decrypt($value, 'DES', $key,  0, $iv); };
    }
717335
public static function isSecure() {
        $s = &$_SERVER;
        $ssl = (!empty($s['HTTPS']) && $s['HTTPS'] == 'on') ? true : false;
        return $ssl;
    }
1271514
public function checkFsockopen()
    {
        $result = 1;
        $iErrNo = 0;
        $sErrStr = '';
        if ($oRes = @fsockopen('www.example.com', 80, $iErrNo, $sErrStr, 10)) {
            $result = 2;
            fclose($oRes);
        }
        return $result;
    }
1350228
private function checkKey()
    {
        if (empty($this->key) || empty($this->authentication)) {
            $key = $this->secureRandomKey();
            $message = 'No binary key provided to encrypt/decrypt data.';

            if ($key !== null) {
                $message .= sprintf(" For example: base64_decode('%s')", base64_encode($key));
            }

            throw new RuntimeException($message);
        }
    }
425762
public function parse()
	{
		if ($this->compressed_size >= $this->min_compressed_size)
		{
			// Check ID1, ID2, and CM
			if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
			{
				return false;
			}

			// Get the FLG (FLaGs)
			$this->flags = ord($this->compressed_data[3]);

			// FLG bits above (1 << 4) are reserved
			if ($this->flags > 0x1F)
			{
				return false;
			}

			// Advance the pointer after the above
			$this->position += 4;

			// MTIME
			$mtime = substr($this->compressed_data, $this->position, 4);
			// Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
			if (current(unpack('S', "\x00\x01")) === 1)
			{
				$mtime = strrev($mtime);
			}
			$this->MTIME = current(unpack('l', $mtime));
			$this->position += 4;

			// Get the XFL (eXtra FLags)
			$this->XFL = ord($this->compressed_data[$this->position++]);

			// Get the OS (Operating System)
			$this->OS = ord($this->compressed_data[$this->position++]);

			// Parse the FEXTRA
			if ($this->flags & 4)
			{
				// Read subfield IDs
				$this->SI1 = $this->compressed_data[$this->position++];
				$this->SI2 = $this->compressed_data[$this->position++];

				// SI2 set to zero is reserved for future use
				if ($this->SI2 === "\x00")
				{
					return false;
				}

				// Get the length of the extra field
				$len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
				$this->position += 2;

				// Check the length of the string is still valid
				$this->min_compressed_size += $len + 4;
				if ($this->compressed_size >= $this->min_compressed_size)
				{
					// Set the extra field to the given data
					$this->extra_field = substr($this->compressed_data, $this->position, $len);
					$this->position += $len;
				}
				else
				{
					return false;
				}
			}

			// Parse the FNAME
			if ($this->flags & 8)
			{
				// Get the length of the filename
				$len = strcspn($this->compressed_data, "\x00", $this->position);

				// Check the length of the string is still valid
				$this->min_compressed_size += $len + 1;
				if ($this->compressed_size >= $this->min_compressed_size)
				{
					// Set the original filename to the given string
					$this->filename = substr($this->compressed_data, $this->position, $len);
					$this->position += $len + 1;
				}
				else
				{
					return false;
				}
			}

			// Parse the FCOMMENT
			if ($this->flags & 16)
			{
				// Get the length of the comment
				$len = strcspn($this->compressed_data, "\x00", $this->position);

				// Check the length of the string is still valid
				$this->min_compressed_size += $len + 1;
				if ($this->compressed_size >= $this->min_compressed_size)
				{
					// Set the original comment to the given string
					$this->comment = substr($this->compressed_data, $this->position, $len);
					$this->position += $len + 1;
				}
				else
				{
					return false;
				}
			}

			// Parse the FHCRC
			if ($this->flags & 2)
			{
				// Check the length of the string is still valid
				$this->min_compressed_size += $len + 2;
				if ($this->compressed_size >= $this->min_compressed_size)
				{
					// Read the CRC
					$crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));

					// Check the CRC matches
					if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
					{
						$this->position += 2;
					}
					else
					{
						return false;
					}
				}
				else
				{
					return false;
				}
			}

			// Decompress the actual data
			if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
			{
				return false;
			}
			else
			{
				$this->position = $this->compressed_size - 8;
			}

			// Check CRC of data
			$crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
			$this->position += 4;
			/*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
			{
				return false;
			}*/

			// Check ISIZE of data
			$isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
			$this->position += 4;
			if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
			{
				return false;
			}

			// Wow, against all odds, we've actually got a valid gzip string
			return true;
		}
		else
		{
			return false;
		}
	}
1053754
protected static function openssl_random_pseudo_bytes_exists()
    {

        if (!isset(self::$openssl_random_pseudo_bytes_exists)) {
            self::$openssl_random_pseudo_bytes_exists = function_exists('openssl_random_pseudo_bytes') &&
                (version_compare(PHP_VERSION, '5.3.4') >= 0 || !self::is_windows());
        }

        return self::$openssl_random_pseudo_bytes_exists;
    }
158536
public function checkSecure()
    {
        if ($this->secure === null)
            return true;

        $in_secure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';
        return ($this->secure && $in_secure) || (! $this->secure && ! $in_secure);
    }
142804
public static function EnableSSL () {
		return static::LasyClassVarConstAccessCallback('enable_ssl', function () {
			return (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') || (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
		});
	}
1316897
function isValidEngine($engine)
    {
        if ($engine == Base::ENGINE_OPENSSL) {
            if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
                $this->cipher_name_openssl = 'rc4-40';
            } else {
                switch (strlen($this->key)) {
                    case 5:
                        $this->cipher_name_openssl = 'rc4-40';
                        break;
                    case 8:
                        $this->cipher_name_openssl = 'rc4-64';
                        break;
                    case 16:
                        $this->cipher_name_openssl = 'rc4';
                        break;
                    default:
                        return false;
                }
            }
        }

        return parent::isValidEngine($engine);
    }
399068
public function isSecure()
    {
        $https = $this->server('HTTPS');
        return !empty($https) && 'off' !== strtolower($https) || (int)$this->server('SERVER_PORT') === 443 || $this->server('HTTP_X_FORWARDED_PROTO') === 'https';
    }
690631
protected function checkCtrl()
    {
        $requestURL = substr($this->request, 0, -38); //the last 38 characters are the CTRL param
        $hashInput = strlen($requestURL) . $requestURL;
        $this->debugMessage[] = 'REQUEST URL: ' . $requestURL;
        $this->debugMessage[] = 'GET ctrl: ' . @$this->getData['ctrl'];
        $this->debugMessage[] = 'Calculated ctrl: ' . $this->hmac($this->secretKey, $hashInput);
        if (isset($this->getData['ctrl']) && $this->getData['ctrl'] == $this->hmac($this->secretKey, $hashInput)) {
            return true;
        }
        $this->errorMessage[] = 'HASH: Calculated hash is not valid!';
        $this->errorMessage[] = 'BACKREF ERROR: ' . @$this->getData['err'];
        return false;
    }