OLD | NEW |
| (Empty) |
1 // Copyright 2004-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "omaha/base/registry_hive.h" | |
17 #include "omaha/base/accounts.h" | |
18 #include "omaha/base/reg_key.h" | |
19 #include "omaha/base/safe_format.h" | |
20 #include "omaha/base/string.h" | |
21 #include "omaha/base/system.h" | |
22 #include "omaha/base/user_info.h" | |
23 #include "omaha/base/utils.h" | |
24 | |
25 namespace omaha { | |
26 | |
27 RegistryHive::RegistryHive() | |
28 : hive_holding_key_(NULL) { | |
29 } | |
30 | |
31 RegistryHive::~RegistryHive() { | |
32 if ( !hive_name_.IsEmpty() ) { | |
33 UnloadHive(); | |
34 } | |
35 } | |
36 | |
37 // Loads hive for requested SID. SID should be in format "S-X-X....." | |
38 // name is a name under which the hive will be added to the HKEY_USERS, | |
39 // you could use persons name here. This parameter should not have '\\' | |
40 // characters! | |
41 // It is not recommended to load more than one hive at once - LoadHive, | |
42 // manipulate hive, UnloadHive, and then work on the next one. | |
43 // | |
44 // Hive could be already loaded: you logged out from other user but system | |
45 // had open key in your current user. In that case I open hive_holding_key_ to | |
46 // prevent system from unloading hive and do not load/unload hive - the system | |
47 // will do it. | |
48 HRESULT RegistryHive::LoadHive(TCHAR const * sid, TCHAR const *name) { | |
49 ASSERT1(sid); | |
50 ASSERT1(name); | |
51 ASSERT1(hive_name_.IsEmpty()); | |
52 ASSERT1(String_FindChar(name, _T('\\')) == -1); | |
53 | |
54 CString profile_key; | |
55 CString hive_path; | |
56 | |
57 // Need set SE_RESTORE_NAME/SE_BACKUP_NAME priveleges to current process | |
58 // otherwise loading of the hive will fail | |
59 RET_IF_FAILED(System::AdjustPrivilege(SE_RESTORE_NAME, true)); | |
60 RET_IF_FAILED(System::AdjustPrivilege(SE_BACKUP_NAME, true)); | |
61 | |
62 SafeCStringFormat(&profile_key, kProfileKeyFormat, sid); | |
63 | |
64 if ( FAILED(RegKey::GetValue(profile_key, kProfilePathValue, &hive_path)) ) { | |
65 return E_FAIL; | |
66 } | |
67 | |
68 | |
69 wchar_t temporary_buffer[MAX_PATH]; | |
70 DWORD ret = ExpandEnvironmentStrings(hive_path, temporary_buffer, MAX_PATH); | |
71 | |
72 if ( !ret || ret >= MAX_PATH ) { | |
73 return E_FAIL; | |
74 } | |
75 hive_path = temporary_buffer; | |
76 | |
77 hive_path.Append(_T("\\")); | |
78 hive_path.Append(kHiveName); | |
79 | |
80 hive_name_ = name; | |
81 | |
82 LONG res = RegLoadKey(HKEY_USERS, hive_name_, hive_path); | |
83 | |
84 if ( ERROR_SHARING_VIOLATION == res ) { | |
85 // It is quite possible that the hive is still held by system. | |
86 hive_name_ = sid; | |
87 | |
88 // if it is the case, this call will succeeed, and the system will not | |
89 // unload the hive while there are outstanding keys opened. | |
90 res = RegOpenKeyEx(HKEY_USERS, | |
91 hive_name_, | |
92 0, | |
93 KEY_ALL_ACCESS, | |
94 &hive_holding_key_); | |
95 } | |
96 | |
97 return (res == ERROR_SUCCESS) ? S_OK : E_FAIL; | |
98 } | |
99 | |
100 // Loads hive for requested SID, but only if the SID is another user | |
101 // (since we don't need to do anything if the sid is ours) | |
102 HRESULT RegistryHive::LoadHive(TCHAR const * user_sid) { | |
103 ASSERT1(user_sid != NULL); | |
104 bool other_user = false; | |
105 | |
106 // Determine if the SID passed in is really another user | |
107 CString current_user_sid; | |
108 HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, ¤t_user_sid); | |
109 if (FAILED(hr)) { | |
110 UTIL_LOG(LE, (_T("[RegistryHive::LoadHive - failed to get current user"))); | |
111 return hr; | |
112 } | |
113 | |
114 // user_sid is the current user - no need to load the hive | |
115 if (lstrcmpi(current_user_sid, user_sid) == 0) | |
116 return S_FALSE; | |
117 | |
118 // Get info on the sid we're being asked to load for | |
119 CString name; | |
120 CString domain; | |
121 SID_NAME_USE user_type; | |
122 hr = accounts::GetUserInfo(user_sid, &name, &domain, &user_type); | |
123 if ( FAILED(hr) || user_type != SidTypeUser ) { | |
124 // Either Sid no longer exists or Sid is not a user Sid | |
125 // (There is other possibility: Sid could be for roaming profile on domain | |
126 // which is currently down, but we do not support roaming profiles) | |
127 return FAILED(hr) ? hr : E_FAIL; | |
128 } | |
129 | |
130 hr = LoadHive(user_sid, name); // Use user name as a temporary key name. | |
131 if ( FAILED(hr) ) { | |
132 // Hive no longer present. | |
133 return E_FAIL; | |
134 } | |
135 | |
136 return S_OK; | |
137 } | |
138 | |
139 | |
140 // Unloads and saves loaded hive | |
141 HRESULT RegistryHive::UnloadHive() { | |
142 if (hive_name_.IsEmpty()) | |
143 return S_OK; | |
144 | |
145 LONG res; | |
146 if ( hive_holding_key_ ) { | |
147 res = RegCloseKey(hive_holding_key_); | |
148 hive_holding_key_ = NULL; | |
149 // no need to unload hive. System will do it. | |
150 } else { | |
151 res = RegUnLoadKey(HKEY_USERS, hive_name_); | |
152 } | |
153 hive_name_.Empty(); | |
154 return (res == ERROR_SUCCESS) ? S_OK : E_FAIL; | |
155 } | |
156 | |
157 // Does it recursively. The name should be relative to HKEY_CURRENT_USER. | |
158 HRESULT RegistryHive::DeleteUserKey(TCHAR const * key_name) { | |
159 ASSERT(key_name && *key_name, (L"")); | |
160 if ( !key_name || !*key_name ) { | |
161 return E_FAIL; | |
162 } | |
163 CString key(key_name); | |
164 ExpandKeyName(&key); | |
165 | |
166 if ( !RegKey::SafeKeyNameForDeletion(key) ) { | |
167 return E_FAIL; | |
168 } | |
169 | |
170 return RegKey::DeleteKey(key); | |
171 } | |
172 | |
173 void RegistryHive::ExpandKeyName(CString * str) { | |
174 ASSERT1(str); | |
175 | |
176 // If we haven't loaded another user's hive, use HKCU instead of | |
177 // HKEY_USERS | |
178 CString key_name; | |
179 if (hive_name_.IsEmpty()) { | |
180 key_name = _T("HKCU\\"); | |
181 } else { | |
182 key_name = _T("HKEY_USERS\\"); | |
183 key_name.Append(hive_name_ + _T("\\")); | |
184 } | |
185 | |
186 key_name.Append(*str); | |
187 *str = key_name; | |
188 } | |
189 | |
190 // Load a user registry | |
191 int LoadUserRegistry(const TCHAR* user_sid, | |
192 ProcessUserRegistryFunc* handler, | |
193 LONG_PTR param) { | |
194 ASSERT1(user_sid && *user_sid); | |
195 ASSERT1(handler); | |
196 | |
197 // Get current user SID | |
198 CString curr_user_sid; | |
199 HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &curr_user_sid); | |
200 if (FAILED(hr)) { | |
201 UTIL_LOG(LE, (_T("[LoadUserRegistry - can't get current user][0x%x]"), hr)); | |
202 return 0; | |
203 } | |
204 | |
205 // Is current user? | |
206 bool other_user = curr_user_sid.CompareNoCase(user_sid) != 0; | |
207 | |
208 // Get the hive for this user | |
209 RegistryHive user_hive; | |
210 if (other_user) { | |
211 // Get the info about this user | |
212 SID_NAME_USE user_type = SidTypeInvalid; | |
213 CString name, domain; | |
214 hr = accounts::GetUserInfo(user_sid, &name, &domain, &user_type); | |
215 if (FAILED(hr) || user_type != SidTypeUser) { | |
216 // Either SID no longer exists or SID is not a user Sid | |
217 // (There is other possibility: SID could be for roaming profile on domain | |
218 // which is currently down, but we do not support roaming profiles) | |
219 UTIL_LOG(LEVEL_WARNING, | |
220 (_T("[LoadUserRegistry - SID %s invalid or unsupported][0x%x]"), | |
221 user_sid, hr)); | |
222 return 0; | |
223 } | |
224 | |
225 // Load the hive | |
226 hr = user_hive.LoadHive(user_sid, domain + _T("_") + name); | |
227 if (FAILED(hr)) { | |
228 // Hive no longer present. | |
229 UTIL_LOG(LW, (_T("[LoadUserRegistry]") | |
230 _T("[hive not present for %s][0x%x]"), user_sid, hr)); | |
231 return 0; | |
232 } | |
233 } | |
234 | |
235 // Get the registry key path | |
236 CString user_reg_path; | |
237 user_hive.ExpandKeyName(&user_reg_path); | |
238 | |
239 // Call the handler | |
240 int res = (*handler)(user_sid, user_reg_path, param); | |
241 | |
242 // Unload the hive | |
243 if (other_user) { | |
244 hr = user_hive.UnloadHive(); | |
245 if (FAILED(hr)) { | |
246 UTIL_LOG(LE, (_T("[LoadUserRegistry]") | |
247 _T("[failed to save hive for %s][0x%x]"), user_sid, hr)); | |
248 } | |
249 } | |
250 | |
251 return res; | |
252 } | |
253 | |
254 // Enumerate all user registries | |
255 void EnumerateAllUserRegistries(ProcessUserRegistryFunc* handler, | |
256 LONG_PTR param) { | |
257 ASSERT1(handler); | |
258 | |
259 CSimpleArray<CString> sid_array; | |
260 accounts::GetAllUserSids(&sid_array); | |
261 for (int i = 0 ; i < sid_array.GetSize() ; ++i) { | |
262 if (LoadUserRegistry(sid_array[i], handler, param)) { | |
263 return; | |
264 } | |
265 } | |
266 } | |
267 | |
268 } // namespace omaha | |
269 | |
OLD | NEW |