OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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/browser/policy/cloud_policy_cache.h" | |
6 | |
7 #include <limits> | |
8 | |
9 #include "base/file_util.h" | |
10 #include "base/logging.h" | |
11 #include "base/task.h" | |
12 #include "base/values.h" | |
13 #include "chrome/browser/browser_thread.h" | |
14 #include "chrome/browser/policy/proto/cloud_policy.pb.h" | |
15 #include "chrome/browser/policy/proto/device_management_backend.pb.h" | |
16 #include "chrome/browser/policy/proto/device_management_constants.h" | |
17 #include "chrome/browser/policy/proto/device_management_local.pb.h" | |
18 // configuration_policy_type.h is generated. See policy_templates.json for | |
19 // policy definitions. | |
20 #include "policy/configuration_policy_type.h" | |
21 | |
22 using google::protobuf::RepeatedPtrField; | |
23 | |
24 namespace policy { | |
25 | |
26 // Saves policy information to a file. | |
27 class PersistCloudPolicyTask : public Task { | |
28 public: | |
29 PersistCloudPolicyTask(const FilePath& path, | |
30 const em::CloudPolicyResponse* policy_response, | |
31 const bool is_unmanaged) | |
32 : path_(path), | |
33 policy_response_(policy_response), | |
34 is_unmanaged_(is_unmanaged) {} | |
35 | |
36 private: | |
37 // Task override. | |
38 virtual void Run(); | |
39 | |
40 const FilePath path_; | |
41 scoped_ptr<const em::CloudPolicyResponse> policy_response_; | |
42 const bool is_unmanaged_; | |
43 }; | |
44 | |
45 void PersistCloudPolicyTask::Run() { | |
46 std::string data; | |
47 em::CachedCloudPolicyResponse cached_policy; | |
48 if (policy_response_.get()) | |
49 cached_policy.mutable_policy_response()->CopyFrom(*policy_response_); | |
50 if (is_unmanaged_) { | |
51 cached_policy.set_unmanaged(true); | |
52 cached_policy.set_timestamp( | |
53 base::Time::NowFromSystemTime().ToTimeT()); | |
54 } | |
55 if (!cached_policy.SerializeToString(&data)) { | |
56 LOG(WARNING) << "Failed to serialize policy data"; | |
57 return; | |
58 } | |
59 | |
60 int size = data.size(); | |
61 if (file_util::WriteFile(path_, data.c_str(), size) != size) { | |
62 LOG(WARNING) << "Failed to write " << path_.value(); | |
63 return; | |
64 } | |
65 } | |
66 | |
67 CloudPolicyCache::CloudPolicyCache( | |
68 const FilePath& backing_file_path) | |
69 : backing_file_path_(backing_file_path), | |
70 mandatory_policy_(new PolicyMapType), | |
71 recommended_policy_(new PolicyMapType), | |
72 fresh_policy_(false), | |
73 is_unmanaged_(false) { | |
74 } | |
75 | |
76 CloudPolicyCache::~CloudPolicyCache() {} | |
77 | |
78 void CloudPolicyCache::LoadPolicyFromFile() { | |
79 if (!file_util::PathExists(backing_file_path_) || fresh_policy_) { | |
80 return; | |
81 } | |
82 | |
83 // Read the protobuf from the file. | |
84 std::string data; | |
85 if (!file_util::ReadFileToString(backing_file_path_, &data)) { | |
86 LOG(WARNING) << "Failed to read policy data from " | |
87 << backing_file_path_.value(); | |
88 return; | |
89 } | |
90 | |
91 em::CachedCloudPolicyResponse cached_response; | |
92 if (!cached_response.ParseFromArray(data.c_str(), data.size())) { | |
93 LOG(WARNING) << "Failed to parse policy data read from " | |
94 << backing_file_path_.value(); | |
95 return; | |
96 } | |
97 base::Time timestamp; | |
98 is_unmanaged_ = cached_response.unmanaged(); | |
99 // Decode and swap in the new policy information. | |
100 scoped_ptr<PolicyMapType> mandatory_policy_map(new PolicyMapType); | |
101 scoped_ptr<PolicyMapType> recommended_policy_map(new PolicyMapType); | |
102 bool ok = DecodePolicyResponse(cached_response.policy_response(), | |
103 mandatory_policy_map.get(), | |
104 recommended_policy_map.get(), | |
105 ×tamp); | |
106 if (is_unmanaged_) { | |
107 timestamp = base::Time::FromTimeT(cached_response.timestamp()); | |
108 } else { | |
109 if (!ok) { | |
110 LOG(WARNING) << "Decoding policy data failed."; | |
111 return; | |
112 } | |
113 } | |
114 { | |
115 base::AutoLock lock(lock_); | |
116 if (!fresh_policy_) | |
117 mandatory_policy_.reset(mandatory_policy_map.release()); | |
118 recommended_policy_.reset(recommended_policy_map.release()); | |
119 last_policy_refresh_time_ = timestamp; | |
120 } | |
121 } | |
122 | |
123 bool CloudPolicyCache::SetPolicy( | |
124 const em::CloudPolicyResponse& policy) { | |
125 is_unmanaged_ = false; | |
126 base::Time timestamp; | |
127 scoped_ptr<PolicyMapType> mandatory_policy_map(new PolicyMapType); | |
128 scoped_ptr<PolicyMapType> recommended_policy_map(new PolicyMapType); | |
129 bool ok = DecodePolicyResponse(policy, mandatory_policy_map.get(), | |
130 recommended_policy_map.get(), ×tamp); | |
131 if (!ok) { | |
132 // TODO(jkummerow): Signal error to PolicyProvider. | |
133 return false; | |
134 } | |
135 const bool new_policy_differs = | |
136 !Equals(mandatory_policy_map.get(), mandatory_policy_.get()) || | |
137 !Equals(recommended_policy_map.get(), recommended_policy_.get()); | |
138 { | |
139 base::AutoLock lock(lock_); | |
140 mandatory_policy_.reset(mandatory_policy_map.release()); | |
141 recommended_policy_.reset(recommended_policy_map.release()); | |
142 fresh_policy_ = true; | |
143 last_policy_refresh_time_ = timestamp; | |
144 } | |
145 | |
146 em::CloudPolicyResponse* policy_copy = new em::CloudPolicyResponse; | |
147 policy_copy->CopyFrom(policy); | |
148 BrowserThread::PostTask( | |
149 BrowserThread::FILE, | |
150 FROM_HERE, | |
151 new PersistCloudPolicyTask(backing_file_path_, policy_copy, false)); | |
152 return new_policy_differs; | |
153 } | |
154 | |
155 PolicyMapType* CloudPolicyCache::GetPolicy() { | |
156 base::AutoLock lock(lock_); | |
157 PolicyMapType* policy_copy = new PolicyMapType; | |
158 for (PolicyMapType::iterator it = mandatory_policy_->begin(); | |
159 it != mandatory_policy_->end(); ++it) { | |
gfeher
2011/02/02 08:42:45
Nit: indent.
Jakob Kummerow
2011/02/03 14:36:52
Done.
| |
160 policy_copy->insert(std::make_pair(it->first, it->second->DeepCopy())); | |
161 } | |
162 return policy_copy; | |
163 } | |
164 | |
165 void CloudPolicyCache::SetUnmanaged() { | |
166 is_unmanaged_ = true; | |
167 { | |
168 base::AutoLock lock(lock_); | |
169 mandatory_policy_.reset(new PolicyMapType); | |
170 recommended_policy_.reset(new PolicyMapType); | |
171 last_policy_refresh_time_ = base::Time::NowFromSystemTime(); | |
172 } | |
173 BrowserThread::PostTask( | |
174 BrowserThread::FILE, | |
175 FROM_HERE, | |
176 new PersistCloudPolicyTask(backing_file_path_, NULL, true)); | |
177 } | |
178 | |
179 // static | |
180 bool CloudPolicyCache::DecodePolicyResponse( | |
181 const em::CloudPolicyResponse& policy_response, | |
182 PolicyMapType* mandatory, | |
183 PolicyMapType* recommended, | |
184 base::Time* timestamp) { | |
185 std::string data = policy_response.signed_response(); | |
186 | |
187 if (!VerifySignature(policy_response.signature(), data, | |
188 policy_response.certificate_chain())) { | |
189 LOG(WARNING) << "Failed to verify signature."; | |
190 return false; | |
191 } | |
192 | |
193 em::SignedCloudPolicyResponse response; | |
194 if (!response.ParseFromArray(data.c_str(), data.size())) { | |
195 LOG(WARNING) << "Failed to parse SignedCloudPolicyResponse protobuf."; | |
196 return false; | |
197 } | |
198 | |
199 // TODO(jkummerow): Verify response.device_token(). Needs final specification | |
200 // which token we're actually sending / expecting to get back. | |
201 | |
202 // TODO(jkummerow): Store response.device_name(), if we decide to transfer | |
203 // it from the server to the client. | |
204 | |
205 // Reject policies that claim to be from the future. | |
206 DCHECK(timestamp); | |
207 *timestamp = base::Time::FromTimeT(response.timestamp()); | |
208 if (*timestamp > base::Time::NowFromSystemTime()) { | |
gfeher
2011/02/02 08:42:45
Allow a small difference for safety?
Jakob Kummerow
2011/02/03 14:36:52
Done.
| |
209 LOG(WARNING) << "Rejected policy data, timestamp is from the future."; | |
210 return false; | |
211 } | |
212 | |
213 DecodePolicy(response.settings(), mandatory, recommended); | |
214 return true; | |
215 } | |
216 | |
217 // static | |
218 bool CloudPolicyCache::VerifySignature( | |
219 const std::string& signature, | |
220 const std::string& data, | |
221 const RepeatedPtrField<std::string>& certificate_chain) { | |
222 // TODO(jkummerow): Implement this. Non-trivial because we want to do it | |
223 // for all platforms -> it's enough work to deserve its own CL. | |
224 // Don't forget to also verify the hostname of the server against the cert. | |
225 return true; | |
226 } | |
227 | |
228 // static | |
229 bool CloudPolicyCache::Equals(const PolicyMapType* a, const PolicyMapType* b) { | |
230 for (PolicyMapType::const_iterator it = a->begin(); it != a->end(); ++it) { | |
231 PolicyMapType::const_iterator is_in_b = b->find(it->first); | |
232 if (is_in_b == b->end()) | |
233 return false; | |
234 if (!it->second->Equals(is_in_b->second)) | |
235 return false; | |
236 } | |
237 for (PolicyMapType::const_iterator it = b->begin(); it != b->end(); ++it) { | |
238 if (a->find(it->first) == a->end()) | |
239 return false; | |
240 } | |
241 return true; | |
242 } | |
243 | |
244 } // namespace policy | |
OLD | NEW |