| // Copyright 2011 Google Inc. All Rights Reserved. |
| // Use of this source code is governed by an Apache-style license that can be |
| // found in the COPYING file. |
| // |
| // A library to manage RLZ information for access-points shared |
| // across different client applications. |
| |
| #include "rlz/win/lib/rlz_lib.h" |
| |
| #include <windows.h> |
| #include <aclapi.h> |
| #include <winbase.h> |
| #include <winerror.h> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/win/registry.h" |
| #include "base/win/windows_version.h" |
| #include "rlz/lib/assert.h" |
| #include "rlz/lib/financial_ping.h" |
| #include "rlz/lib/lib_values.h" |
| #include "rlz/lib/rlz_value_store.h" |
| #include "rlz/lib/string_utils.h" |
| #include "rlz/win/lib/lib_mutex.h" |
| #include "rlz/win/lib/machine_deal.h" |
| #include "rlz/win/lib/user_key.h" |
| |
| namespace { |
| |
| // Path to recursively copy into the replacemment hives. These are needed |
| // to make sure certain win32 APIs continue to run correctly once the real |
| // hives are replaced. |
| const wchar_t* kHKLMAccessProviders = |
| L"System\\CurrentControlSet\\Control\\Lsa\\AccessProviders"; |
| |
| // Helper functions |
| |
| // Deletes a registry key if it exists and has no subkeys or values. |
| // TODO: Move this to a registry_utils file and add unittest. |
| bool DeleteKeyIfEmpty(HKEY root_key, const wchar_t* key_name) { |
| if (!key_name) { |
| ASSERT_STRING("DeleteKeyIfEmpty: key_name is NULL"); |
| return false; |
| } else { // Scope needed for RegKey |
| base::win::RegKey key(root_key, key_name, KEY_READ); |
| if (!key.Valid()) |
| return true; // Key does not exist - nothing to do. |
| |
| base::win::RegistryKeyIterator key_iter(root_key, key_name); |
| if (key_iter.SubkeyCount() > 0) |
| return true; // Not empty, so nothing to do |
| |
| base::win::RegistryValueIterator value_iter(root_key, key_name); |
| if (value_iter.ValueCount() > 0) |
| return true; // Not empty, so nothing to do |
| } |
| |
| // The key is empty - delete it now. |
| base::win::RegKey key(root_key, L"", KEY_WRITE); |
| return key.DeleteKey(key_name) == ERROR_SUCCESS; |
| } |
| |
| LONG GetProductEventsAsCgiHelper(rlz_lib::Product product, char* cgi, |
| size_t cgi_size, |
| rlz_lib::RlzValueStore* store) { |
| // Prepend the CGI param key to the buffer. |
| std::string cgi_arg; |
| base::StringAppendF(&cgi_arg, "%s=", rlz_lib::kEventsCgiVariable); |
| if (cgi_size <= cgi_arg.size()) |
| return ERROR_MORE_DATA; |
| |
| size_t index; |
| for (index = 0; index < cgi_arg.size(); ++index) |
| cgi[index] = cgi_arg[index]; |
| |
| // Read stored events. |
| std::vector<std::string> events; |
| if (!store->ReadProductEvents(product, &events)) |
| return ERROR_PATH_NOT_FOUND; |
| |
| // Append the events to the buffer. |
| size_t buffer_size = cgi_size - cgi_arg.size(); |
| size_t num_values = 0; |
| LONG result = ERROR_SUCCESS; |
| |
| for (num_values = 0; num_values < events.size(); ++num_values) { |
| cgi[index] = '\0'; |
| |
| int divider = num_values > 0 ? 1 : 0; |
| DWORD size = cgi_size - (index + divider); |
| if (size <= 0) |
| return ERROR_MORE_DATA; |
| |
| strncpy(cgi + index + divider, events[num_values].c_str(), size); |
| if (divider) |
| cgi[index] = rlz_lib::kEventsCgiSeparator; |
| |
| index += std::min((DWORD)events[num_values].length(), size) + divider; |
| } |
| |
| cgi[index] = '\0'; |
| |
| if (result == ERROR_MORE_DATA) |
| return result; |
| |
| return num_values > 0 ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; |
| } |
| |
| bool ClearAllProductEventValues(rlz_lib::Product product, const wchar_t* key) { |
| rlz_lib::LibMutex lock; |
| if (lock.failed()) |
| return false; |
| |
| rlz_lib::UserKey user_key; |
| if (!user_key.HasAccess(true)) |
| return false; |
| |
| const wchar_t* product_name = rlz_lib::GetProductName(product); |
| if (!product_name) |
| return false; |
| |
| base::win::RegKey reg_key; |
| rlz_lib::GetEventsRegKey(key, NULL, KEY_WRITE, ®_key); |
| reg_key.DeleteKey(product_name); |
| |
| // Verify that the value no longer exists. |
| base::win::RegKey product_events(reg_key.Handle(), product_name, KEY_READ); |
| if (product_events.Valid()) { |
| ASSERT_STRING("ClearAllProductEvents: Key deletion failed"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void CopyRegistryTree(const base::win::RegKey& src, base::win::RegKey* dest) { |
| // First copy values. |
| for (base::win::RegistryValueIterator i(src.Handle(), L""); |
| i.Valid(); ++i) { |
| dest->WriteValue(i.Name(), reinterpret_cast<const void*>(i.Value()), |
| i.ValueSize(), i.Type()); |
| } |
| |
| // Next copy subkeys recursively. |
| for (base::win::RegistryKeyIterator i(src.Handle(), L""); |
| i.Valid(); ++i) { |
| base::win::RegKey subkey(dest->Handle(), i.Name(), KEY_ALL_ACCESS); |
| CopyRegistryTree(base::win::RegKey(src.Handle(), i.Name(), KEY_READ), |
| &subkey); |
| } |
| } |
| |
| } // namespace anonymous |
| |
| |
| namespace rlz_lib { |
| |
| bool GetProductEventsAsCgi(Product product, char* cgi, size_t cgi_size) { |
| if (!cgi || cgi_size <= 0) { |
| ASSERT_STRING("GetProductEventsAsCgi: Invalid buffer"); |
| return false; |
| } |
| |
| cgi[0] = 0; |
| |
| ScopedRlzValueStoreLock lock; |
| RlzValueStore* store = lock.GetStore(); |
| if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) |
| return false; |
| |
| DWORD size_local = |
| cgi_size <= kMaxCgiLength + 1 ? cgi_size : kMaxCgiLength + 1; |
| UINT length = 0; |
| LONG result = GetProductEventsAsCgiHelper(product, cgi, size_local, store); |
| if (result == ERROR_MORE_DATA && cgi_size >= (kMaxCgiLength + 1)) |
| result = ERROR_SUCCESS; |
| |
| if (result != ERROR_SUCCESS) { |
| if (result == ERROR_MORE_DATA) |
| ASSERT_STRING("GetProductEventsAsCgi: Insufficient buffer size"); |
| cgi[0] = 0; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ClearAllProductEvents(Product product) { |
| bool result; |
| |
| result = ClearAllProductEventValues(product, kEventsSubkeyName); |
| result &= ClearAllProductEventValues(product, kStatefulEventsSubkeyName); |
| return result; |
| } |
| |
| |
| // OEM Deal confirmation storage functions. |
| |
| template<class T> |
| class typed_buffer_ptr { |
| scoped_array<char> buffer_; |
| |
| public: |
| typed_buffer_ptr() { |
| } |
| |
| explicit typed_buffer_ptr(size_t size) : buffer_(new char[size]) { |
| } |
| |
| void reset(size_t size) { |
| buffer_.reset(new char[size]); |
| } |
| |
| operator T*() { |
| return reinterpret_cast<T*>(buffer_.get()); |
| } |
| }; |
| |
| // Check if this SID has the desired access by scanning the ACEs in the DACL. |
| // This function is part of the rlz_lib namespace so that it can be called from |
| // unit tests. Non-unit test code should not call this function. |
| bool HasAccess(PSID sid, ACCESS_MASK access_mask, ACL* dacl) { |
| if (dacl == NULL) |
| return false; |
| |
| ACL_SIZE_INFORMATION info; |
| if (!GetAclInformation(dacl, &info, sizeof(info), AclSizeInformation)) |
| return false; |
| |
| GENERIC_MAPPING generic_mapping = {KEY_READ, KEY_WRITE, KEY_EXECUTE, |
| KEY_ALL_ACCESS}; |
| MapGenericMask(&access_mask, &generic_mapping); |
| |
| for (DWORD i = 0; i < info.AceCount; ++i) { |
| ACCESS_ALLOWED_ACE* ace; |
| if (GetAce(dacl, i, reinterpret_cast<void**>(&ace))) { |
| if ((ace->Header.AceFlags & INHERIT_ONLY_ACE) == INHERIT_ONLY_ACE) |
| continue; |
| |
| PSID existing_sid = reinterpret_cast<PSID>(&ace->SidStart); |
| DWORD mask = ace->Mask; |
| MapGenericMask(&mask, &generic_mapping); |
| |
| if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE && |
| (mask & access_mask) == access_mask && EqualSid(existing_sid, sid)) |
| return true; |
| |
| if (ace->Header.AceType == ACCESS_DENIED_ACE_TYPE && |
| (mask & access_mask) != 0 && EqualSid(existing_sid, sid)) |
| return false; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool CreateMachineState() { |
| LibMutex lock; |
| if (lock.failed()) |
| return false; |
| |
| base::win::RegKey hklm_key; |
| if (hklm_key.Create(HKEY_LOCAL_MACHINE, kLibKeyName, |
| KEY_ALL_ACCESS | KEY_WOW64_32KEY) != ERROR_SUCCESS) { |
| ASSERT_STRING("rlz_lib::CreateMachineState: " |
| "Unable to create / open machine key."); |
| return false; |
| } |
| |
| // Create a SID that represents ALL USERS. |
| DWORD users_sid_size = SECURITY_MAX_SID_SIZE; |
| typed_buffer_ptr<SID> users_sid(users_sid_size); |
| CreateWellKnownSid(WinBuiltinUsersSid, NULL, users_sid, &users_sid_size); |
| |
| // Get the security descriptor for the registry key. |
| DWORD original_sd_size = 0; |
| ::RegGetKeySecurity(hklm_key.Handle(), DACL_SECURITY_INFORMATION, NULL, |
| &original_sd_size); |
| typed_buffer_ptr<SECURITY_DESCRIPTOR> original_sd(original_sd_size); |
| |
| LONG result = ::RegGetKeySecurity(hklm_key.Handle(), |
| DACL_SECURITY_INFORMATION, original_sd, &original_sd_size); |
| if (result != ERROR_SUCCESS) { |
| ASSERT_STRING("rlz_lib::CreateMachineState: " |
| "Unable to create / open machine key."); |
| return false; |
| } |
| |
| // Make a copy of the security descriptor so we can modify it. The one |
| // returned by RegGetKeySecurity() is self-relative, so we need to make it |
| // absolute. |
| DWORD new_sd_size = 0; |
| DWORD dacl_size = 0; |
| DWORD sacl_size = 0; |
| DWORD owner_size = 0; |
| DWORD group_size = 0; |
| ::MakeAbsoluteSD(original_sd, NULL, &new_sd_size, NULL, &dacl_size, |
| NULL, &sacl_size, NULL, &owner_size, |
| NULL, &group_size); |
| |
| typed_buffer_ptr<SECURITY_DESCRIPTOR> new_sd(new_sd_size); |
| // Make sure the DACL is big enough to add one more ACE. |
| typed_buffer_ptr<ACL> dacl(dacl_size + SECURITY_MAX_SID_SIZE); |
| typed_buffer_ptr<ACL> sacl(sacl_size); |
| typed_buffer_ptr<SID> owner(owner_size); |
| typed_buffer_ptr<SID> group(group_size); |
| |
| if (!::MakeAbsoluteSD(original_sd, new_sd, &new_sd_size, dacl, &dacl_size, |
| sacl, &sacl_size, owner, &owner_size, |
| group, &group_size)) { |
| ASSERT_STRING("rlz_lib::CreateMachineState: MakeAbsoluteSD failed"); |
| return false; |
| } |
| |
| // If all users already have read/write access to the registry key, then |
| // nothing to do. Otherwise change the security descriptor of the key to |
| // give everyone access. |
| if (HasAccess(users_sid, KEY_ALL_ACCESS, dacl)) { |
| return false; |
| } |
| |
| // Add ALL-USERS ALL-ACCESS ACL. |
| EXPLICIT_ACCESS ea; |
| ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); |
| ea.grfAccessPermissions = GENERIC_ALL | KEY_ALL_ACCESS; |
| ea.grfAccessMode = GRANT_ACCESS; |
| ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT; |
| ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME; |
| ea.Trustee.ptstrName = L"Everyone"; |
| |
| ACL* new_dacl = NULL; |
| result = SetEntriesInAcl(1, &ea, dacl, &new_dacl); |
| if (result != ERROR_SUCCESS) { |
| ASSERT_STRING("rlz_lib::CreateMachineState: SetEntriesInAcl failed"); |
| return false; |
| } |
| |
| BOOL ok = SetSecurityDescriptorDacl(new_sd, TRUE, new_dacl, FALSE); |
| if (!ok) { |
| ASSERT_STRING("rlz_lib::CreateMachineState: " |
| "SetSecurityDescriptorOwner failed"); |
| LocalFree(new_dacl); |
| return false; |
| } |
| |
| result = ::RegSetKeySecurity(hklm_key.Handle(), |
| DACL_SECURITY_INFORMATION, |
| new_sd); |
| // Note that the new DACL cannot be freed until after the call to |
| // RegSetKeySecurity(). |
| LocalFree(new_dacl); |
| |
| bool success = true; |
| if (result != ERROR_SUCCESS) { |
| ASSERT_STRING("rlz_lib::CreateMachineState: " |
| "Unable to create / open machine key."); |
| success = false; |
| } |
| |
| |
| return success; |
| } |
| |
| bool SetMachineDealCode(const char* dcc) { |
| return MachineDealCode::Set(dcc); |
| } |
| |
| bool GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size) { |
| return MachineDealCode::GetAsCgi(cgi, cgi_size); |
| } |
| |
| bool GetMachineDealCode(char* dcc, size_t dcc_size) { |
| return MachineDealCode::Get(dcc, dcc_size); |
| } |
| |
| // Combined functions. |
| |
| bool SetMachineDealCodeFromPingResponse(const char* response) { |
| return MachineDealCode::SetFromPingResponse(response); |
| } |
| |
| void ClearProductState(Product product, const AccessPoint* access_points) { |
| LibMutex lock; |
| if (lock.failed()) |
| return; |
| |
| UserKey user_key; |
| if (!user_key.HasAccess(true)) |
| return; |
| |
| // Delete all product specific state. |
| VERIFY(ClearAllProductEvents(product)); |
| VERIFY(FinancialPing::ClearLastPingTime(product)); |
| |
| // Delete all RLZ's for access points being uninstalled. |
| if (access_points) { |
| for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) { |
| VERIFY(SetAccessPointRlz(access_points[i], "")); |
| } |
| } |
| |
| // Delete each of the known subkeys if empty. |
| const wchar_t* subkeys[] = { |
| kRlzsSubkeyName, |
| kEventsSubkeyName, |
| kStatefulEventsSubkeyName, |
| kPingTimesSubkeyName |
| }; |
| |
| for (int i = 0; i < arraysize(subkeys); i++) { |
| std::wstring subkey_name; |
| base::StringAppendF(&subkey_name, L"%ls\\%ls", kLibKeyName, subkeys[i]); |
| SupplementaryBranding::AppendBrandToString(&subkey_name); |
| |
| VERIFY(DeleteKeyIfEmpty(user_key.Get(), subkey_name.c_str())); |
| } |
| |
| // Delete the library key and its parents too now if empty. |
| VERIFY(DeleteKeyIfEmpty(user_key.Get(), kLibKeyName)); |
| VERIFY(DeleteKeyIfEmpty(user_key.Get(), kGoogleCommonKeyName)); |
| VERIFY(DeleteKeyIfEmpty(user_key.Get(), kGoogleKeyName)); |
| } |
| |
| bool GetMachineId(wchar_t* buffer, size_t buffer_size) { |
| if (!buffer || buffer_size <= kMachineIdLength) |
| return false; |
| buffer[0] = 0; |
| |
| std::wstring machine_id; |
| if (!MachineDealCode::GetMachineId(&machine_id)) |
| return false; |
| |
| wcsncpy(buffer, machine_id.c_str(), buffer_size); |
| buffer[buffer_size - 1] = 0; |
| return true; |
| } |
| |
| void InitializeTempHivesForTesting(const base::win::RegKey& temp_hklm_key, |
| const base::win::RegKey& temp_hkcu_key) { |
| // For the moment, the HKCU hive requires no initialization. |
| |
| if (base::win::GetVersion() >= base::win::VERSION_WIN7) { |
| // Copy the following HKLM subtrees to the temporary location so that the |
| // win32 APIs used by the tests continue to work: |
| // |
| // HKLM\System\CurrentControlSet\Control\Lsa\AccessProviders |
| // |
| // This seems to be required since Win7. |
| base::win::RegKey dest(temp_hklm_key.Handle(), kHKLMAccessProviders, |
| KEY_ALL_ACCESS); |
| CopyRegistryTree(base::win::RegKey(HKEY_LOCAL_MACHINE, |
| kHKLMAccessProviders, |
| KEY_READ), |
| &dest); |
| } |
| } |
| |
| } // namespace rlz_lib |