[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/Requests/ -> Ssl.php (source)

   1  <?php
   2  /**
   3   * SSL utilities for Requests
   4   *
   5   * @package Requests\Utilities
   6   */
   7  
   8  namespace WpOrg\Requests;
   9  
  10  use WpOrg\Requests\Exception\InvalidArgument;
  11  use WpOrg\Requests\Utility\InputValidator;
  12  
  13  /**
  14   * SSL utilities for Requests
  15   *
  16   * Collection of utilities for working with and verifying SSL certificates.
  17   *
  18   * @package Requests\Utilities
  19   */
  20  final class Ssl {
  21      /**
  22       * Verify the certificate against common name and subject alternative names
  23       *
  24       * Unfortunately, PHP doesn't check the certificate against the alternative
  25       * names, leading things like 'https://www.github.com/' to be invalid.
  26       *
  27       * @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
  28       *
  29       * @param string|Stringable $host Host name to verify against
  30       * @param array $cert Certificate data from openssl_x509_parse()
  31       * @return bool
  32       * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $host argument is not a string or a stringable object.
  33       * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $cert argument is not an array or array accessible.
  34       */
  35  	public static function verify_certificate($host, $cert) {
  36          if (InputValidator::is_string_or_stringable($host) === false) {
  37              throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
  38          }
  39  
  40          if (InputValidator::has_array_access($cert) === false) {
  41              throw InvalidArgument::create(2, '$cert', 'array|ArrayAccess', gettype($cert));
  42          }
  43  
  44          $has_dns_alt = false;
  45  
  46          // Check the subjectAltName
  47          if (!empty($cert['extensions']['subjectAltName'])) {
  48              $altnames = explode(',', $cert['extensions']['subjectAltName']);
  49              foreach ($altnames as $altname) {
  50                  $altname = trim($altname);
  51                  if (strpos($altname, 'DNS:') !== 0) {
  52                      continue;
  53                  }
  54  
  55                  $has_dns_alt = true;
  56  
  57                  // Strip the 'DNS:' prefix and trim whitespace
  58                  $altname = trim(substr($altname, 4));
  59  
  60                  // Check for a match
  61                  if (self::match_domain($host, $altname) === true) {
  62                      return true;
  63                  }
  64              }
  65  
  66              if ($has_dns_alt === true) {
  67                  return false;
  68              }
  69          }
  70  
  71          // Fall back to checking the common name if we didn't get any dNSName
  72          // alt names, as per RFC2818
  73          if (!empty($cert['subject']['CN'])) {
  74              // Check for a match
  75              return (self::match_domain($host, $cert['subject']['CN']) === true);
  76          }
  77  
  78          return false;
  79      }
  80  
  81      /**
  82       * Verify that a reference name is valid
  83       *
  84       * Verifies a dNSName for HTTPS usage, (almost) as per Firefox's rules:
  85       * - Wildcards can only occur in a name with more than 3 components
  86       * - Wildcards can only occur as the last character in the first
  87       *   component
  88       * - Wildcards may be preceded by additional characters
  89       *
  90       * We modify these rules to be a bit stricter and only allow the wildcard
  91       * character to be the full first component; that is, with the exclusion of
  92       * the third rule.
  93       *
  94       * @param string|Stringable $reference Reference dNSName
  95       * @return boolean Is the name valid?
  96       * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
  97       */
  98  	public static function verify_reference_name($reference) {
  99          if (InputValidator::is_string_or_stringable($reference) === false) {
 100              throw InvalidArgument::create(1, '$reference', 'string|Stringable', gettype($reference));
 101          }
 102  
 103          if ($reference === '') {
 104              return false;
 105          }
 106  
 107          if (preg_match('`\s`', $reference) > 0) {
 108              // Whitespace detected. This can never be a dNSName.
 109              return false;
 110          }
 111  
 112          $parts = explode('.', $reference);
 113          if ($parts !== array_filter($parts)) {
 114              // DNSName cannot contain two dots next to each other.
 115              return false;
 116          }
 117  
 118          // Check the first part of the name
 119          $first = array_shift($parts);
 120  
 121          if (strpos($first, '*') !== false) {
 122              // Check that the wildcard is the full part
 123              if ($first !== '*') {
 124                  return false;
 125              }
 126  
 127              // Check that we have at least 3 components (including first)
 128              if (count($parts) < 2) {
 129                  return false;
 130              }
 131          }
 132  
 133          // Check the remaining parts
 134          foreach ($parts as $part) {
 135              if (strpos($part, '*') !== false) {
 136                  return false;
 137              }
 138          }
 139  
 140          // Nothing found, verified!
 141          return true;
 142      }
 143  
 144      /**
 145       * Match a hostname against a dNSName reference
 146       *
 147       * @param string|Stringable $host Requested host
 148       * @param string|Stringable $reference dNSName to match against
 149       * @return boolean Does the domain match?
 150       * @throws \WpOrg\Requests\Exception\InvalidArgument When either of the passed arguments is not a string or a stringable object.
 151       */
 152  	public static function match_domain($host, $reference) {
 153          if (InputValidator::is_string_or_stringable($host) === false) {
 154              throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
 155          }
 156  
 157          // Check if the reference is blocklisted first
 158          if (self::verify_reference_name($reference) !== true) {
 159              return false;
 160          }
 161  
 162          // Check for a direct match
 163          if ((string) $host === (string) $reference) {
 164              return true;
 165          }
 166  
 167          // Calculate the valid wildcard match if the host is not an IP address
 168          // Also validates that the host has 3 parts or more, as per Firefox's ruleset,
 169          // as a wildcard reference is only allowed with 3 parts or more, so the
 170          // comparison will never match if host doesn't contain 3 parts or more as well.
 171          if (ip2long($host) === false) {
 172              $parts    = explode('.', $host);
 173              $parts[0] = '*';
 174              $wildcard = implode('.', $parts);
 175              if ($wildcard === (string) $reference) {
 176                  return true;
 177              }
 178          }
 179  
 180          return false;
 181      }
 182  }


Generated: Mon Dec 6 01:00:03 2021 Cross-referenced by PHPXref 0.7.1