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