1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/updater/device_management/dm_message.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "chrome/updater/device_management/dm_cached_policy_info.h"
13 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
14 #include "components/policy/proto/device_management_backend.pb.h"
15 #include "crypto/signature_verifier.h"
16 #include "third_party/boringssl/src/include/openssl/rsa.h"
17
18 namespace updater {
19
20 namespace {
21
VerifySHA256Signature(const std::string & data,const std::string & key,const std::string & signature)22 bool VerifySHA256Signature(const std::string& data,
23 const std::string& key,
24 const std::string& signature) {
25 crypto::SignatureVerifier verifier;
26 if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA256,
27 base::as_bytes(base::make_span(signature)),
28 base::as_bytes(base::make_span(key)))) {
29 VLOG(1) << "Invalid verification signature/key format.";
30 return false;
31 }
32 verifier.VerifyUpdate(base::as_bytes(base::make_span(data)));
33 return verifier.VerifyFinal();
34 }
35
36 class DMResponseValidator {
37 public:
38 DMResponseValidator(const CachedPolicyInfo& policy_info,
39 const std::string& expected_dm_token,
40 const std::string& expected_device_id);
41 ~DMResponseValidator() = default;
42
43 static std::unique_ptr<DMResponseValidator> Create(
44 const CachedPolicyInfo& policy_info,
45 const std::string& expected_dm_token,
46 const std::string& expected_device_id,
47 const enterprise_management::DeviceManagementResponse& dm_response);
48
49 // Validates the DM response is correctly signed and intended for this device.
50 bool ValidateResponse(
51 const enterprise_management::DeviceManagementResponse& dm_response) const;
52
53 // Validates a single policy inside the DM response is correctly signed.
54 bool ValidatePolicy(
55 const enterprise_management::PolicyFetchResponse& policy_response) const;
56
57 private:
58 // Extracts the public key for for subsequent policy verification. The public
59 // key is either the new key that signed the response, or the existing public
60 // key if key is not rotated. Possible scenarios:
61 // 1) Client sends the first policy fetch request. In this case, there's no
62 // existing public key on the client side. The server must attach a new
63 // public key in the response and the new key must be signed by the pinned
64 // key.
65 // 2) Client sends a policy fetch request with an existing public key, and
66 // server decides to NOT rotate the key. In this case, the response doesn't
67 // have a new key. The signing key will continue to be the existing key.
68 // 3) Client sends a policy fetch request with an existing public key, and
69 // server decides to rotate the key. In this case, the server attaches a
70 // new public key in the response. The new key must be signed by the pinned
71 // key AND the existing key.
72 bool ExtractPublicKey(
73 const enterprise_management::DeviceManagementResponse& dm_response);
74
75 bool ValidateDMToken(
76 const enterprise_management::PolicyData& policy_data) const;
77 bool ValidateDeviceId(
78 const enterprise_management::PolicyData& policy_data) const;
79 bool ValidateTimestamp(
80 const enterprise_management::PolicyData& policy_data) const;
81
82 const CachedPolicyInfo policy_info_;
83 const std::string expected_dm_token_;
84 const std::string expected_device_id_;
85
86 std::string signing_public_key_;
87 };
88
DMResponseValidator(const CachedPolicyInfo & policy_info,const std::string & expected_dm_token,const std::string & expected_device_id)89 DMResponseValidator::DMResponseValidator(const CachedPolicyInfo& policy_info,
90 const std::string& expected_dm_token,
91 const std::string& expected_device_id)
92 : policy_info_(policy_info),
93 expected_dm_token_(expected_dm_token),
94 expected_device_id_(expected_device_id) {}
95
96 // static
Create(const CachedPolicyInfo & policy_info,const std::string & expected_dm_token,const std::string & expected_device_id,const enterprise_management::DeviceManagementResponse & dm_response)97 std::unique_ptr<DMResponseValidator> DMResponseValidator::Create(
98 const CachedPolicyInfo& policy_info,
99 const std::string& expected_dm_token,
100 const std::string& expected_device_id,
101 const enterprise_management::DeviceManagementResponse& dm_response) {
102 auto validator = std::make_unique<DMResponseValidator>(
103 policy_info, expected_dm_token, expected_device_id);
104 if (!validator->ExtractPublicKey(dm_response))
105 return nullptr;
106
107 return validator;
108 }
109
ExtractPublicKey(const enterprise_management::DeviceManagementResponse & dm_response)110 bool DMResponseValidator::ExtractPublicKey(
111 const enterprise_management::DeviceManagementResponse& dm_response) {
112 // We should only extract public key once.
113 DCHECK(signing_public_key_.empty());
114
115 if (!dm_response.has_policy_response() ||
116 dm_response.policy_response().responses_size() == 0) {
117 return false;
118 }
119
120 // We can extract the public key from any of the policy in the response. For
121 // convenience, just use the first policy.
122 const enterprise_management::PolicyFetchResponse& first_policy_response =
123 dm_response.policy_response().responses(0);
124
125 if (!first_policy_response.has_new_public_key_verification_data()) {
126 // No new public key, meaning key is not rotated, so use the existing one.
127 signing_public_key_ = policy_info_.public_key();
128 if (signing_public_key_.empty()) {
129 VLOG(1) << "No existing or new public key, must have one at least.";
130 return false;
131 }
132
133 return true;
134 }
135
136 if (!first_policy_response.has_new_public_key_verification_data_signature()) {
137 VLOG(1) << "New public key doesn't have signature for verification.";
138 return false;
139 }
140
141 // Verifies that the new public key verification data is properly signed
142 // by the pinned key.
143 if (!VerifySHA256Signature(
144 first_policy_response.new_public_key_verification_data(),
145 policy::GetPolicyVerificationKey(),
146 first_policy_response.new_public_key_verification_data_signature())) {
147 VLOG(1) << "Public key verification data is not signed correctly.";
148 return false;
149 }
150
151 // Also validates new public key against the cached public key, if the latter
152 // exists (The server must sign the new key with the previous key).
153 enterprise_management::PublicKeyVerificationData public_key_data;
154 if (!public_key_data.ParseFromString(
155 first_policy_response.new_public_key_verification_data())) {
156 VLOG(1) << "Failed to deserialize new public key.";
157 return false;
158 }
159
160 const std::string existing_key = policy_info_.public_key();
161 if (!existing_key.empty()) {
162 if (!first_policy_response.has_new_public_key_signature() ||
163 !VerifySHA256Signature(
164 public_key_data.new_public_key(), existing_key,
165 first_policy_response.new_public_key_signature())) {
166 VLOG(1) << "Key verification against cached public key failed.";
167 return false;
168 }
169 }
170
171 // Now that the new public key has been successfully verified, we rotate to
172 // use it for future policy data validation.
173 VLOG(1) << "Accepting a public key for domain: " << public_key_data.domain();
174 signing_public_key_ = public_key_data.new_public_key();
175 return true;
176 }
177
ValidateDMToken(const enterprise_management::PolicyData & policy_data) const178 bool DMResponseValidator::ValidateDMToken(
179 const enterprise_management::PolicyData& policy_data) const {
180 if (!policy_data.has_request_token()) {
181 VLOG(1) << "No DMToken in PolicyData.";
182 return false;
183 }
184
185 const std::string& received_token = policy_data.request_token();
186 if (!base::EqualsCaseInsensitiveASCII(received_token, expected_dm_token_)) {
187 VLOG(1) << "Unexpected DMToken: expected " << expected_dm_token_
188 << ", received " << received_token;
189 return false;
190 }
191
192 return true;
193 }
194
ValidateDeviceId(const enterprise_management::PolicyData & policy_data) const195 bool DMResponseValidator::ValidateDeviceId(
196 const enterprise_management::PolicyData& policy_data) const {
197 if (!policy_data.has_device_id()) {
198 VLOG(1) << "No Device Id in PolicyData.";
199 return false;
200 }
201
202 const std::string& received_id = policy_data.device_id();
203 if (!base::EqualsCaseInsensitiveASCII(received_id, expected_device_id_)) {
204 VLOG(1) << "Unexpected Device Id: expected " << expected_device_id_
205 << ", received " << received_id;
206 return false;
207 }
208
209 return true;
210 }
211
ValidateTimestamp(const enterprise_management::PolicyData & policy_data) const212 bool DMResponseValidator::ValidateTimestamp(
213 const enterprise_management::PolicyData& policy_data) const {
214 if (!policy_data.has_timestamp()) {
215 VLOG(1) << "No timestamp in PolicyData.";
216 return false;
217 }
218
219 if (policy_data.timestamp() < policy_info_.timestamp()) {
220 VLOG(1) << "Unexpected DM response timestamp older than cached timestamp.";
221 return false;
222 }
223
224 return true;
225 }
226
ValidateResponse(const enterprise_management::DeviceManagementResponse & dm_response) const227 bool DMResponseValidator::ValidateResponse(
228 const enterprise_management::DeviceManagementResponse& dm_response) const {
229 if (!dm_response.has_policy_response() ||
230 dm_response.policy_response().responses_size() == 0) {
231 return false;
232 }
233
234 // We can validate the DM response with any of the policy in it. For
235 // convenience, just use the first policy.
236 const enterprise_management::PolicyFetchResponse& first_policy_response =
237 dm_response.policy_response().responses(0);
238
239 enterprise_management::PolicyData policy_data;
240 if (!policy_data.ParseFromString(first_policy_response.policy_data())) {
241 VLOG(1) << "Failed to deserialize policy data.";
242 return false;
243 }
244
245 return ValidateDMToken(policy_data) && ValidateDeviceId(policy_data) &&
246 ValidateTimestamp(policy_data);
247 }
248
ValidatePolicy(const enterprise_management::PolicyFetchResponse & policy_response) const249 bool DMResponseValidator::ValidatePolicy(
250 const enterprise_management::PolicyFetchResponse& policy_response) const {
251 if (!policy_response.has_policy_data()) {
252 VLOG(1) << "Policy entry does not have data.";
253 return false;
254 }
255
256 if (!policy_response.has_policy_data_signature()) {
257 VLOG(1) << "Policy entry does not have verification signature.";
258 return false;
259 }
260
261 const std::string& policy_data = policy_response.policy_data();
262 if (!VerifySHA256Signature(policy_data, signing_public_key_,
263 policy_response.policy_data_signature())) {
264 VLOG(1) << "Policy entry are not correctly signed.";
265 return false;
266 }
267
268 // Our signing key signs both the policy data and the previous key. In
269 // theory it is possible to have a cross-protocol attack here: attacker can
270 // take a signed public key and claim it is the policy data. Check that
271 // the policy data is not a public key to defend against such attacks.
272 bssl::UniquePtr<RSA> public_key(RSA_public_key_from_bytes(
273 reinterpret_cast<const uint8_t*>(policy_data.data()),
274 policy_data.length()));
275 if (public_key) {
276 VLOG(1) << "Rejected policy data in public key format.";
277 return false;
278 }
279
280 return true;
281 }
282
283 } // namespace
284
GetRegisterBrowserRequestData(const std::string & machine_name,const std::string & os_platform,const std::string & os_version)285 std::string GetRegisterBrowserRequestData(const std::string& machine_name,
286 const std::string& os_platform,
287 const std::string& os_version) {
288 enterprise_management::DeviceManagementRequest dm_request;
289
290 ::enterprise_management::RegisterBrowserRequest* request =
291 dm_request.mutable_register_browser_request();
292 request->set_machine_name(machine_name);
293 request->set_os_platform(os_platform);
294 request->set_os_version(os_version);
295
296 return dm_request.SerializeAsString();
297 }
298
GetPolicyFetchRequestData(const std::string & policy_type,const CachedPolicyInfo & policy_info)299 std::string GetPolicyFetchRequestData(const std::string& policy_type,
300 const CachedPolicyInfo& policy_info) {
301 enterprise_management::DeviceManagementRequest dm_request;
302
303 enterprise_management::PolicyFetchRequest* policy_fetch_request =
304 dm_request.mutable_policy_request()->add_requests();
305 policy_fetch_request->set_policy_type(policy_type);
306 policy_fetch_request->set_signature_type(
307 enterprise_management::PolicyFetchRequest::SHA256_RSA);
308 policy_fetch_request->set_verification_key_hash(
309 policy::kPolicyVerificationKeyHash);
310
311 if (policy_info.has_key_version()) {
312 policy_fetch_request->set_public_key_version(policy_info.key_version());
313 }
314
315 return dm_request.SerializeAsString();
316 }
317
ParseDeviceRegistrationResponse(const std::string & response_data)318 std::string ParseDeviceRegistrationResponse(const std::string& response_data) {
319 enterprise_management::DeviceManagementResponse dm_response;
320 if (!dm_response.ParseFromString(response_data) ||
321 !dm_response.has_register_response() ||
322 !dm_response.register_response().has_device_management_token()) {
323 return std::string();
324 }
325
326 return dm_response.register_response().device_management_token();
327 }
328
ParsePolicyFetchResponse(const std::string & response_data,const CachedPolicyInfo & policy_info,const std::string & expected_dm_token,const std::string & expected_device_id)329 DMPolicyMap ParsePolicyFetchResponse(const std::string& response_data,
330 const CachedPolicyInfo& policy_info,
331 const std::string& expected_dm_token,
332 const std::string& expected_device_id) {
333 enterprise_management::DeviceManagementResponse dm_response;
334 if (!dm_response.ParseFromString(response_data) ||
335 !dm_response.has_policy_response() ||
336 dm_response.policy_response().responses_size() == 0) {
337 return {};
338 }
339
340 std::unique_ptr<DMResponseValidator> validator = DMResponseValidator::Create(
341 policy_info, expected_dm_token, expected_device_id, dm_response);
342 if (!validator || !validator->ValidateResponse(dm_response)) {
343 return {};
344 }
345
346 // Validate each individual policy and put valid ones into the returned policy
347 // map.
348 DMPolicyMap responses;
349 for (int i = 0; i < dm_response.policy_response().responses_size(); ++i) {
350 const ::enterprise_management::PolicyFetchResponse& response =
351 dm_response.policy_response().responses(i);
352 enterprise_management::PolicyData policy_data;
353 if (!response.has_policy_data() ||
354 !policy_data.ParseFromString(response.policy_data()) ||
355 !policy_data.IsInitialized() || !policy_data.has_policy_type()) {
356 VLOG(1) << "Ignoring invalid PolicyData.";
357 continue;
358 }
359
360 const std::string& policy_type = policy_data.policy_type();
361 if (!validator->ValidatePolicy(response)) {
362 VLOG(1) << "Policy " << policy_type << " validation failed.";
363 continue;
364 }
365
366 if (responses.find(policy_type) != responses.end()) {
367 VLOG(1) << "Duplicate PolicyFetchResponse for type " << policy_type;
368 continue;
369 }
370
371 std::string policy_fetch_response;
372 if (!response.SerializeToString(&policy_fetch_response)) {
373 VLOG(1) << "Failed to serialize policy " << policy_type;
374 continue;
375 }
376
377 responses.emplace(policy_type, std::move(policy_fetch_response));
378 }
379
380 return responses;
381 }
382
383 } // namespace updater
384