PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 7.4.3
Jetpack – WP Security, Backup, Speed, & Growth v7.4.3
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / class.jetpack-data.php
class.jetpack-data.php
225 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class Jetpack_Data {
4 /*
5 * Used internally when we want to look for the Normal Blog Token
6 * without knowing its token key ahead of time.
7 */
8 const MAGIC_NORMAL_TOKEN_KEY = ';normal;';
9
10 /**
11 * Gets the requested token.
12 *
13 * Tokens are one of two types:
14 * 1. Blog Tokens: These are the "main" tokens. Each site typically has one Blog Token,
15 * though some sites can have multiple "Special" Blog Tokens (see below). These tokens
16 * are not associated with a user account. They represent the site's connection with
17 * the Jetpack servers.
18 * 2. User Tokens: These are "sub-"tokens. Each connected user account has one User Token.
19 *
20 * All tokens look like "{$token_key}.{$private}". $token_key is a public ID for the
21 * token, and $private is a secret that should never be displayed anywhere or sent
22 * over the network; it's used only for signing things.
23 *
24 * Blog Tokens can be "Normal" or "Special".
25 * * Normal: The result of a normal connection flow. They look like
26 * "{$random_string_1}.{$random_string_2}"
27 * That is, $token_key and $private are both random strings.
28 * Sites only have one Normal Blog Token. Normal Tokens are found in either
29 * Jetpack_Options::get_option( 'blog_token' ) (usual) or the JETPACK_BLOG_TOKEN
30 * constant (rare).
31 * * Special: A connection token for sites that have gone through an alternative
32 * connection flow. They look like:
33 * ";{$special_id}{$special_version};{$wpcom_blog_id};.{$random_string}"
34 * That is, $private is a random string and $token_key has a special structure with
35 * lots of semicolons.
36 * Most sites have zero Special Blog Tokens. Special tokens are only found in the
37 * JETPACK_BLOG_TOKEN constant.
38 *
39 * In particular, note that Normal Blog Tokens never start with ";" and that
40 * Special Blog Tokens always do.
41 *
42 * When searching for a matching Blog Tokens, Blog Tokens are examined in the following
43 * order:
44 * 1. Defined Special Blog Tokens (via the JETPACK_BLOG_TOKEN constant)
45 * 2. Stored Normal Tokens (via Jetpack_Options::get_option( 'blog_token' ))
46 * 3. Defined Normal Tokens (via the JETPACK_BLOG_TOKEN constant)
47 *
48 * @param int|false $user_id false: Return the Blog Token. int: Return that user's User Token.
49 * @param string|false $token_key If provided, check that the token matches the provided input.
50 * false : Use first token. Default.
51 * Jetpack_Data::MAGIC_NORMAL_TOKEN_KEY : Use first Normal Token.
52 * non-empty string : Use matching token
53 * @return object|false
54 */
55 public static function get_access_token( $user_id = false, $token_key = false ) {
56 $possible_special_tokens = array();
57 $possible_normal_tokens = array();
58
59 if ( $user_id ) {
60 if ( !$user_tokens = Jetpack_Options::get_option( 'user_tokens' ) ) {
61 return false;
62 }
63 if ( $user_id === JETPACK_MASTER_USER ) {
64 if ( !$user_id = Jetpack_Options::get_option( 'master_user' ) ) {
65 return false;
66 }
67 }
68 if ( !isset( $user_tokens[$user_id] ) || ! $user_tokens[$user_id] ) {
69 return false;
70 }
71 $user_token_chunks = explode( '.', $user_tokens[$user_id] );
72 if ( empty( $user_token_chunks[1] ) || empty( $user_token_chunks[2] ) ) {
73 return false;
74 }
75 if ( $user_id != $user_token_chunks[2] ) {
76 return false;
77 }
78 $possible_normal_tokens[] = "{$user_token_chunks[0]}.{$user_token_chunks[1]}";
79 } else {
80 $stored_blog_token = Jetpack_Options::get_option( 'blog_token' );
81 if ( $stored_blog_token ) {
82 $possible_normal_tokens[] = $stored_blog_token;
83 }
84
85 $defined_tokens = Jetpack_Constants::is_defined( 'JETPACK_BLOG_TOKEN' )
86 ? explode( ',', Jetpack_Constants::get_constant( 'JETPACK_BLOG_TOKEN' ) )
87 : array();
88
89 foreach ( $defined_tokens as $defined_token ) {
90 if ( ';' === $defined_token[0] ) {
91 $possible_special_tokens[] = $defined_token;
92 } else {
93 $possible_normal_tokens[] = $defined_token;
94 }
95 }
96 }
97
98 if ( self::MAGIC_NORMAL_TOKEN_KEY === $token_key ) {
99 $possible_tokens = $possible_normal_tokens;
100 } else {
101 $possible_tokens = array_merge( $possible_special_tokens, $possible_normal_tokens );
102 }
103
104 if ( ! $possible_tokens ) {
105 return false;
106 }
107
108 $valid_token = false;
109
110 if ( false === $token_key ) {
111 // Use first token.
112 $valid_token = $possible_tokens[0];
113 } elseif ( self::MAGIC_NORMAL_TOKEN_KEY === $token_key ) {
114 // Use first normal token.
115 $valid_token = $possible_tokens[0]; // $possible_tokens only contains normal tokens because of earlier check.
116 } else {
117 // Use the token matching $token_key or false if none.
118 // Ensure we check the full key.
119 $token_check = rtrim( $token_key, '.' ) . '.';
120
121 foreach ( $possible_tokens as $possible_token ) {
122 if ( hash_equals( substr( $possible_token, 0, strlen( $token_check ) ), $token_check ) ) {
123 $valid_token = $possible_token;
124 break;
125 }
126 }
127 }
128
129 if ( ! $valid_token ) {
130 return false;
131 }
132
133 return (object) array(
134 'secret' => $valid_token,
135 'external_user_id' => (int) $user_id,
136 );
137 }
138
139 /**
140 * This function mirrors Jetpack_Data::is_usable_domain() in the WPCOM codebase.
141 *
142 * @param $domain
143 * @param array $extra
144 *
145 * @return bool|WP_Error
146 */
147 public static function is_usable_domain( $domain, $extra = array() ) {
148
149 // If it's empty, just fail out.
150 if ( ! $domain ) {
151 return new WP_Error( 'fail_domain_empty', sprintf( __( 'Domain `%1$s` just failed is_usable_domain check as it is empty.', 'jetpack' ), $domain ) );
152 }
153
154 /**
155 * Skips the usuable domain check when connecting a site.
156 *
157 * Allows site administrators with domains that fail gethostname-based checks to pass the request to WP.com
158 *
159 * @since 4.1.0
160 *
161 * @param bool If the check should be skipped. Default false.
162 */
163 if ( apply_filters( 'jetpack_skip_usuable_domain_check', false ) ) {
164 return true;
165 }
166
167 // None of the explicit localhosts.
168 $forbidden_domains = array(
169 'wordpress.com',
170 'localhost',
171 'localhost.localdomain',
172 '127.0.0.1',
173 'local.wordpress.test', // VVV
174 'local.wordpress-trunk.test', // VVV
175 'src.wordpress-develop.test', // VVV
176 'build.wordpress-develop.test', // VVV
177 );
178 if ( in_array( $domain, $forbidden_domains ) ) {
179 return new WP_Error( 'fail_domain_forbidden', sprintf( __( 'Domain `%1$s` just failed is_usable_domain check as it is in the forbidden array.', 'jetpack' ), $domain ) );
180 }
181
182 // No .test or .local domains
183 if ( preg_match( '#\.(test|local)$#i', $domain ) ) {
184 return new WP_Error( 'fail_domain_tld', sprintf( __( 'Domain `%1$s` just failed is_usable_domain check as it uses an invalid top level domain.', 'jetpack' ), $domain ) );
185 }
186
187 // No WPCOM subdomains
188 if ( preg_match( '#\.wordpress\.com$#i', $domain ) ) {
189 return new WP_Error( 'fail_subdomain_wpcom', sprintf( __( 'Domain `%1$s` just failed is_usable_domain check as it is a subdomain of WordPress.com.', 'jetpack' ), $domain ) );
190 }
191
192 // If PHP was compiled without support for the Filter module (very edge case)
193 if ( ! function_exists( 'filter_var' ) ) {
194 // Just pass back true for now, and let wpcom sort it out.
195 return true;
196 }
197
198 return true;
199 }
200
201 /**
202 * Returns true if the IP address passed in should not be in a reserved range, even if PHP says that it is.
203 * See: https://bugs.php.net/bug.php?id=66229 and https://github.com/php/php-src/commit/d1314893fd1325ca6aa0831101896e31135a2658
204 *
205 * This function mirrors Jetpack_Data::php_bug_66229_check() in the WPCOM codebase.
206 */
207 public static function php_bug_66229_check( $ip ) {
208 if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
209 return false;
210 }
211
212 $ip_arr = array_map( 'intval', explode( '.', $ip ) );
213
214 if ( 128 == $ip_arr[0] && 0 == $ip_arr[1] ) {
215 return true;
216 }
217
218 if ( 191 == $ip_arr[0] && 255 == $ip_arr[1] ) {
219 return true;
220 }
221
222 return false;
223 }
224 }
225