| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/policy/configuration_policy_provider_win.h" | 5 #include "chrome/browser/policy/configuration_policy_provider_win.h" |
| 6 | 6 |
| 7 #include <userenv.h> | 7 #include "chrome/browser/policy/asynchronous_policy_provider.h" |
| 8 | 8 #include "chrome/browser/policy/configuration_policy_loader_win.h" |
| 9 #include <algorithm> | 9 #include "chrome/browser/policy/configuration_policy_provider_delegate_win.h" |
| 10 | |
| 11 #include "base/logging.h" | |
| 12 #include "base/object_watcher.h" | |
| 13 #include "base/scoped_ptr.h" | |
| 14 #include "base/string_number_conversions.h" | |
| 15 #include "base/string_piece.h" | |
| 16 #include "base/string_util.h" | |
| 17 #include "base/sys_string_conversions.h" | |
| 18 #include "base/thread_restrictions.h" | |
| 19 #include "base/utf_string_conversions.h" | |
| 20 #include "base/values.h" | |
| 21 #include "base/win/registry.h" | |
| 22 #include "chrome/common/policy_constants.h" | |
| 23 | |
| 24 using base::win::RegKey; | |
| 25 | 10 |
| 26 namespace policy { | 11 namespace policy { |
| 27 | 12 |
| 28 namespace { | |
| 29 | |
| 30 bool ReadRegistryStringValue(RegKey* key, const string16& name, | |
| 31 string16* result) { | |
| 32 DWORD value_size = 0; | |
| 33 DWORD key_type = 0; | |
| 34 scoped_array<uint8> buffer; | |
| 35 | |
| 36 if (!key->ReadValue(name.c_str(), 0, &value_size, &key_type)) | |
| 37 return false; | |
| 38 if (key_type != REG_SZ) | |
| 39 return false; | |
| 40 | |
| 41 // According to the Microsoft documentation, the string | |
| 42 // buffer may not be explicitly 0-terminated. Allocate a | |
| 43 // slightly larger buffer and pre-fill to zeros to guarantee | |
| 44 // the 0-termination. | |
| 45 buffer.reset(new uint8[value_size + 2]); | |
| 46 memset(buffer.get(), 0, value_size + 2); | |
| 47 key->ReadValue(name.c_str(), buffer.get(), &value_size, NULL); | |
| 48 result->assign(reinterpret_cast<const wchar_t*>(buffer.get())); | |
| 49 return true; | |
| 50 } | |
| 51 | |
| 52 } // namespace | |
| 53 | |
| 54 // Period at which to run the reload task in case the group policy change | 13 // Period at which to run the reload task in case the group policy change |
| 55 // watchers fail. | 14 // watchers fail. |
| 56 const int kReloadIntervalMinutes = 15; | 15 const int kReloadIntervalMinutes = 15; |
| 57 | 16 |
| 58 ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: | |
| 59 GroupPolicyChangeWatcher( | |
| 60 base::WeakPtr<ConfigurationPolicyProviderWin> provider, | |
| 61 int reload_interval_minutes) | |
| 62 : provider_(provider), | |
| 63 user_policy_changed_event_(false, false), | |
| 64 machine_policy_changed_event_(false, false), | |
| 65 user_policy_watcher_failed_(false), | |
| 66 machine_policy_watcher_failed_(false), | |
| 67 reload_interval_minutes_(reload_interval_minutes), | |
| 68 reload_task_(NULL) { | |
| 69 if (!RegisterGPNotification(user_policy_changed_event_.handle(), false)) { | |
| 70 PLOG(WARNING) << "Failed to register user group policy notification"; | |
| 71 user_policy_watcher_failed_ = true; | |
| 72 } | |
| 73 if (!RegisterGPNotification(machine_policy_changed_event_.handle(), true)) { | |
| 74 PLOG(WARNING) << "Failed to register machine group policy notification."; | |
| 75 machine_policy_watcher_failed_ = true; | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: | |
| 80 ~GroupPolicyChangeWatcher() { | |
| 81 if (MessageLoop::current()) | |
| 82 MessageLoop::current()->RemoveDestructionObserver(this); | |
| 83 DCHECK(!reload_task_); | |
| 84 } | |
| 85 | |
| 86 void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher::Start() { | |
| 87 MessageLoop::current()->AddDestructionObserver(this); | |
| 88 SetupWatches(); | |
| 89 } | |
| 90 | |
| 91 void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher::Stop() { | |
| 92 user_policy_watcher_.StopWatching(); | |
| 93 machine_policy_watcher_.StopWatching(); | |
| 94 if (reload_task_) { | |
| 95 reload_task_->Cancel(); | |
| 96 reload_task_ = NULL; | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher::SetupWatches() { | |
| 101 if (reload_task_) { | |
| 102 reload_task_->Cancel(); | |
| 103 reload_task_ = NULL; | |
| 104 } | |
| 105 | |
| 106 if (!user_policy_watcher_failed_) { | |
| 107 if (!user_policy_watcher_.GetWatchedObject() && | |
| 108 !user_policy_watcher_.StartWatching( | |
| 109 user_policy_changed_event_.handle(), this)) { | |
| 110 LOG(WARNING) << "Failed to start watch for user policy change event"; | |
| 111 user_policy_watcher_failed_ = true; | |
| 112 } | |
| 113 } | |
| 114 if (!machine_policy_watcher_failed_) { | |
| 115 if (!machine_policy_watcher_.GetWatchedObject() && | |
| 116 !machine_policy_watcher_.StartWatching( | |
| 117 machine_policy_changed_event_.handle(), this)) { | |
| 118 LOG(WARNING) << "Failed to start watch for machine policy change event"; | |
| 119 machine_policy_watcher_failed_ = true; | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 if (user_policy_watcher_failed_ || machine_policy_watcher_failed_) { | |
| 124 reload_task_ = | |
| 125 NewRunnableMethod(this, &GroupPolicyChangeWatcher::ReloadFromTask); | |
| 126 int64 delay = | |
| 127 base::TimeDelta::FromMinutes(reload_interval_minutes_).InMilliseconds(); | |
| 128 MessageLoop::current()->PostDelayedTask(FROM_HERE, reload_task_, delay); | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher::Reload() { | |
| 133 if (provider_.get()) | |
| 134 provider_->NotifyStoreOfPolicyChange(); | |
| 135 SetupWatches(); | |
| 136 } | |
| 137 | |
| 138 void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: | |
| 139 ReloadFromTask() { | |
| 140 // Make sure to not call Cancel() on the task, since it might hold the last | |
| 141 // reference that keeps this object alive. | |
| 142 reload_task_ = NULL; | |
| 143 Reload(); | |
| 144 } | |
| 145 | |
| 146 void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: | |
| 147 OnObjectSignaled(HANDLE object) { | |
| 148 DCHECK(object == user_policy_changed_event_.handle() || | |
| 149 object == machine_policy_changed_event_.handle()) | |
| 150 << "unexpected object signaled policy reload, obj = " | |
| 151 << std::showbase << std::hex << object; | |
| 152 Reload(); | |
| 153 } | |
| 154 | |
| 155 void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: | |
| 156 WillDestroyCurrentMessageLoop() { | |
| 157 reload_task_ = NULL; | |
| 158 MessageLoop::current()->RemoveDestructionObserver(this); | |
| 159 } | |
| 160 | |
| 161 ConfigurationPolicyProviderWin::ConfigurationPolicyProviderWin( | 17 ConfigurationPolicyProviderWin::ConfigurationPolicyProviderWin( |
| 162 const PolicyDefinitionList* policy_list) | 18 const PolicyDefinitionList* policy_list) |
| 163 : ConfigurationPolicyProvider(policy_list) { | 19 : AsynchronousPolicyProvider( |
| 164 watcher_ = new GroupPolicyChangeWatcher(this->AsWeakPtr(), | 20 policy_list, |
| 165 kReloadIntervalMinutes); | 21 new ConfigurationPolicyLoaderWin( |
| 166 watcher_->Start(); | 22 new ConfigurationPolicyProviderDelegateWin(policy_list), |
| 167 } | 23 kReloadIntervalMinutes)) {} |
| 168 | |
| 169 ConfigurationPolicyProviderWin::~ConfigurationPolicyProviderWin() { | |
| 170 watcher_->Stop(); | |
| 171 } | |
| 172 | |
| 173 bool ConfigurationPolicyProviderWin::GetRegistryPolicyString( | |
| 174 const string16& name, string16* result) const { | |
| 175 string16 path = string16(kRegistrySubKey); | |
| 176 RegKey policy_key; | |
| 177 // First try the global policy. | |
| 178 if (policy_key.Open(HKEY_LOCAL_MACHINE, path.c_str(), KEY_READ)) { | |
| 179 if (ReadRegistryStringValue(&policy_key, name, result)) | |
| 180 return true; | |
| 181 policy_key.Close(); | |
| 182 } | |
| 183 // Fall back on user-specific policy. | |
| 184 if (!policy_key.Open(HKEY_CURRENT_USER, path.c_str(), KEY_READ)) | |
| 185 return false; | |
| 186 return ReadRegistryStringValue(&policy_key, name, result); | |
| 187 } | |
| 188 | |
| 189 bool ConfigurationPolicyProviderWin::GetRegistryPolicyStringList( | |
| 190 const string16& key, ListValue* result) const { | |
| 191 string16 path = string16(kRegistrySubKey); | |
| 192 path += ASCIIToUTF16("\\") + key; | |
| 193 RegKey policy_key; | |
| 194 if (!policy_key.Open(HKEY_LOCAL_MACHINE, path.c_str(), KEY_READ)) { | |
| 195 policy_key.Close(); | |
| 196 // Fall back on user-specific policy. | |
| 197 if (!policy_key.Open(HKEY_CURRENT_USER, path.c_str(), KEY_READ)) | |
| 198 return false; | |
| 199 } | |
| 200 string16 policy_string; | |
| 201 int index = 0; | |
| 202 while (ReadRegistryStringValue(&policy_key, base::IntToString16(++index), | |
| 203 &policy_string)) { | |
| 204 result->Append(Value::CreateStringValue(policy_string)); | |
| 205 } | |
| 206 return true; | |
| 207 } | |
| 208 | |
| 209 bool ConfigurationPolicyProviderWin::GetRegistryPolicyBoolean( | |
| 210 const string16& value_name, bool* result) const { | |
| 211 DWORD value; | |
| 212 RegKey hkcu_policy_key(HKEY_LOCAL_MACHINE, kRegistrySubKey, KEY_READ); | |
| 213 if (hkcu_policy_key.ReadValueDW(value_name.c_str(), &value)) { | |
| 214 *result = value != 0; | |
| 215 return true; | |
| 216 } | |
| 217 | |
| 218 RegKey hklm_policy_key(HKEY_CURRENT_USER, kRegistrySubKey, KEY_READ); | |
| 219 if (hklm_policy_key.ReadValueDW(value_name.c_str(), &value)) { | |
| 220 *result = value != 0; | |
| 221 return true; | |
| 222 } | |
| 223 return false; | |
| 224 } | |
| 225 | |
| 226 bool ConfigurationPolicyProviderWin::GetRegistryPolicyInteger( | |
| 227 const string16& value_name, uint32* result) const { | |
| 228 DWORD value; | |
| 229 RegKey hkcu_policy_key(HKEY_LOCAL_MACHINE, kRegistrySubKey, KEY_READ); | |
| 230 if (hkcu_policy_key.ReadValueDW(value_name.c_str(), &value)) { | |
| 231 *result = value; | |
| 232 return true; | |
| 233 } | |
| 234 | |
| 235 RegKey hklm_policy_key(HKEY_CURRENT_USER, kRegistrySubKey, KEY_READ); | |
| 236 if (hklm_policy_key.ReadValueDW(value_name.c_str(), &value)) { | |
| 237 *result = value; | |
| 238 return true; | |
| 239 } | |
| 240 return false; | |
| 241 } | |
| 242 | |
| 243 bool ConfigurationPolicyProviderWin::Provide( | |
| 244 ConfigurationPolicyStoreInterface* store) { | |
| 245 // This function calls GetRegistryPolicy* which hit up the registry. Those | |
| 246 // are I/O functions not allowed to be called on the main thread. | |
| 247 // http://crbug.com/66453 | |
| 248 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 249 const PolicyDefinitionList* policy_list(policy_definition_list()); | |
| 250 for (const PolicyDefinitionList::Entry* current = policy_list->begin; | |
| 251 current != policy_list->end; ++current) { | |
| 252 std::wstring name = UTF8ToWide(current->name); | |
| 253 switch (current->value_type) { | |
| 254 case Value::TYPE_STRING: { | |
| 255 std::wstring string_value; | |
| 256 if (GetRegistryPolicyString(name.c_str(), &string_value)) { | |
| 257 store->Apply(current->policy_type, | |
| 258 Value::CreateStringValue(string_value)); | |
| 259 } | |
| 260 break; | |
| 261 } | |
| 262 case Value::TYPE_LIST: { | |
| 263 scoped_ptr<ListValue> list_value(new ListValue); | |
| 264 if (GetRegistryPolicyStringList(name.c_str(), list_value.get())) | |
| 265 store->Apply(current->policy_type, list_value.release()); | |
| 266 break; | |
| 267 } | |
| 268 case Value::TYPE_BOOLEAN: { | |
| 269 bool bool_value; | |
| 270 if (GetRegistryPolicyBoolean(name.c_str(), &bool_value)) { | |
| 271 store->Apply(current->policy_type, | |
| 272 Value::CreateBooleanValue(bool_value)); | |
| 273 } | |
| 274 break; | |
| 275 } | |
| 276 case Value::TYPE_INTEGER: { | |
| 277 uint32 int_value; | |
| 278 if (GetRegistryPolicyInteger(name.c_str(), &int_value)) { | |
| 279 store->Apply(current->policy_type, | |
| 280 Value::CreateIntegerValue(int_value)); | |
| 281 } | |
| 282 break; | |
| 283 } | |
| 284 default: | |
| 285 NOTREACHED(); | |
| 286 return false; | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 return true; | |
| 291 } | |
| 292 | 24 |
| 293 } // namespace policy | 25 } // namespace policy |
| OLD | NEW |