Chromium Code Reviews| 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 |