| 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 |