wp/wp-includes/Requests/SSL.php
changeset 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
       
     1 <?php
       
     2 /**
       
     3  * SSL utilities for Requests
       
     4  *
       
     5  * @package Requests
       
     6  * @subpackage Utilities
       
     7  */
       
     8 
       
     9 /**
       
    10  * SSL utilities for Requests
       
    11  *
       
    12  * Collection of utilities for working with and verifying SSL certificates.
       
    13  *
       
    14  * @package Requests
       
    15  * @subpackage Utilities
       
    16  */
       
    17 class Requests_SSL {
       
    18 	/**
       
    19 	 * Verify the certificate against common name and subject alternative names
       
    20 	 *
       
    21 	 * Unfortunately, PHP doesn't check the certificate against the alternative
       
    22 	 * names, leading things like 'https://www.github.com/' to be invalid.
       
    23 	 * Instead
       
    24 	 *
       
    25 	 * @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
       
    26 	 *
       
    27 	 * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
       
    28 	 * @param string $host Host name to verify against
       
    29 	 * @param array $cert Certificate data from openssl_x509_parse()
       
    30 	 * @return bool
       
    31 	 */
       
    32 	public static function verify_certificate($host, $cert) {
       
    33 		// Calculate the valid wildcard match if the host is not an IP address
       
    34 		$parts = explode('.', $host);
       
    35 		if (ip2long($host) === false) {
       
    36 			$parts[0] = '*';
       
    37 		}
       
    38 		$wildcard = implode('.', $parts);
       
    39 
       
    40 		$has_dns_alt = false;
       
    41 
       
    42 		// Check the subjectAltName
       
    43 		if (!empty($cert['extensions']) && !empty($cert['extensions']['subjectAltName'])) {
       
    44 			$altnames = explode(',', $cert['extensions']['subjectAltName']);
       
    45 			foreach ($altnames as $altname) {
       
    46 				$altname = trim($altname);
       
    47 				if (strpos($altname, 'DNS:') !== 0) {
       
    48 					continue;
       
    49 				}
       
    50 
       
    51 				$has_dns_alt = true;
       
    52 
       
    53 				// Strip the 'DNS:' prefix and trim whitespace
       
    54 				$altname = trim(substr($altname, 4));
       
    55 
       
    56 				// Check for a match
       
    57 				if (self::match_domain($host, $altname) === true) {
       
    58 					return true;
       
    59 				}
       
    60 			}
       
    61 		}
       
    62 
       
    63 		// Fall back to checking the common name if we didn't get any dNSName
       
    64 		// alt names, as per RFC2818
       
    65 		if (!$has_dns_alt && !empty($cert['subject']['CN'])) {
       
    66 			// Check for a match
       
    67 			if (self::match_domain($host, $cert['subject']['CN']) === true) {
       
    68 				return true;
       
    69 			}
       
    70 		}
       
    71 
       
    72 		return false;
       
    73 	}
       
    74 
       
    75 	/**
       
    76 	 * Verify that a reference name is valid
       
    77 	 *
       
    78 	 * Verifies a dNSName for HTTPS usage, (almost) as per Firefox's rules:
       
    79 	 * - Wildcards can only occur in a name with more than 3 components
       
    80 	 * - Wildcards can only occur as the last character in the first
       
    81 	 *   component
       
    82 	 * - Wildcards may be preceded by additional characters
       
    83 	 *
       
    84 	 * We modify these rules to be a bit stricter and only allow the wildcard
       
    85 	 * character to be the full first component; that is, with the exclusion of
       
    86 	 * the third rule.
       
    87 	 *
       
    88 	 * @param string $reference Reference dNSName
       
    89 	 * @return boolean Is the name valid?
       
    90 	 */
       
    91 	public static function verify_reference_name($reference) {
       
    92 		$parts = explode('.', $reference);
       
    93 
       
    94 		// Check the first part of the name
       
    95 		$first = array_shift($parts);
       
    96 
       
    97 		if (strpos($first, '*') !== false) {
       
    98 			// Check that the wildcard is the full part
       
    99 			if ($first !== '*') {
       
   100 				return false;
       
   101 			}
       
   102 
       
   103 			// Check that we have at least 3 components (including first)
       
   104 			if (count($parts) < 2) {
       
   105 				return false;
       
   106 			}
       
   107 		}
       
   108 
       
   109 		// Check the remaining parts
       
   110 		foreach ($parts as $part) {
       
   111 			if (strpos($part, '*') !== false) {
       
   112 				return false;
       
   113 			}
       
   114 		}
       
   115 
       
   116 		// Nothing found, verified!
       
   117 		return true;
       
   118 	}
       
   119 
       
   120 	/**
       
   121 	 * Match a hostname against a dNSName reference
       
   122 	 *
       
   123 	 * @param string $host Requested host
       
   124 	 * @param string $reference dNSName to match against
       
   125 	 * @return boolean Does the domain match?
       
   126 	 */
       
   127 	public static function match_domain($host, $reference) {
       
   128 		// Check if the reference is blacklisted first
       
   129 		if (self::verify_reference_name($reference) !== true) {
       
   130 			return false;
       
   131 		}
       
   132 
       
   133 		// Check for a direct match
       
   134 		if ($host === $reference) {
       
   135 			return true;
       
   136 		}
       
   137 
       
   138 		// Calculate the valid wildcard match if the host is not an IP address
       
   139 		// Also validates that the host has 3 parts or more, as per Firefox's
       
   140 		// ruleset.
       
   141 		if (ip2long($host) === false) {
       
   142 			$parts = explode('.', $host);
       
   143 			$parts[0] = '*';
       
   144 			$wildcard = implode('.', $parts);
       
   145 			if ($wildcard === $reference) {
       
   146 				return true;
       
   147 			}
       
   148 		}
       
   149 
       
   150 		return false;
       
   151 	}
       
   152 }