Wordpress 4.6
[autoinstalls/wordpress.git] / wp-includes / Requests / SSL.php
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 }