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 |