// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/renderer_host/pepper/device_id_fetcher.h" #include "base/files/file_util.h" #include "base/macros.h" #include "base/strings/string_number_conversions.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #if defined(OS_CHROMEOS) #include "chromeos/cryptohome/system_salt_getter.h" #endif #include "components/pref_registry/pref_registry_syncable.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_ppapi_host.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "crypto/encryptor.h" #include "crypto/random.h" #include "crypto/sha2.h" #include "ppapi/c/pp_errors.h" #if defined(ENABLE_RLZ) #include "rlz/lib/machine_id.h" #endif using content::BrowserPpapiHost; using content::BrowserThread; using content::RenderProcessHost; namespace chrome { namespace { const char kDRMIdentifierFile[] = "Pepper DRM ID.0"; const uint32_t kSaltLength = 32; void GetMachineIDAsync( const base::Callback& callback) { #if defined(OS_WIN) && defined(ENABLE_RLZ) std::string result; rlz_lib::GetMachineId(&result); callback.Run(result); #elif defined(OS_CHROMEOS) chromeos::SystemSaltGetter::Get()->GetSystemSalt(callback); #else // Not implemented for other platforms. NOTREACHED(); callback.Run(std::string()); #endif } } // namespace DeviceIDFetcher::DeviceIDFetcher(int render_process_id) : in_progress_(false), render_process_id_(render_process_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); } DeviceIDFetcher::~DeviceIDFetcher() {} bool DeviceIDFetcher::Start(const IDCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (in_progress_) return false; in_progress_ = true; callback_ = callback; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&DeviceIDFetcher::CheckPrefsOnUIThread, this)); return true; } // static void DeviceIDFetcher::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* prefs) { prefs->RegisterBooleanPref(prefs::kEnableDRM, true); prefs->RegisterStringPref(prefs::kDRMSalt, ""); } // static base::FilePath DeviceIDFetcher::GetLegacyDeviceIDPath( const base::FilePath& profile_path) { return profile_path.AppendASCII(kDRMIdentifierFile); } void DeviceIDFetcher::CheckPrefsOnUIThread() { DCHECK_CURRENTLY_ON(BrowserThread::UI); Profile* profile = NULL; RenderProcessHost* render_process_host = RenderProcessHost::FromID(render_process_id_); if (render_process_host && render_process_host->GetBrowserContext()) { profile = Profile::FromBrowserContext(render_process_host->GetBrowserContext()); } if (!profile || profile->IsOffTheRecord() || !profile->GetPrefs()->GetBoolean(prefs::kEnableDRM)) { RunCallbackOnIOThread(std::string(), PP_ERROR_NOACCESS); return; } // Check if the salt pref is set. If it isn't, set it. std::string salt = profile->GetPrefs()->GetString(prefs::kDRMSalt); if (salt.empty()) { uint8_t salt_bytes[kSaltLength]; crypto::RandBytes(salt_bytes, arraysize(salt_bytes)); // Since it will be stored in a string pref, convert it to hex. salt = base::HexEncode(salt_bytes, arraysize(salt_bytes)); profile->GetPrefs()->SetString(prefs::kDRMSalt, salt); } #if defined(OS_CHROMEOS) // Try the legacy path first for ChromeOS. We pass the new salt in as well // in case the legacy id doesn't exist. BrowserThread::PostBlockingPoolTask( FROM_HERE, base::Bind(&DeviceIDFetcher::LegacyComputeOnBlockingPool, this, profile->GetPath(), salt)); #else // Get the machine ID and call ComputeOnUIThread with salt + machine_id. GetMachineIDAsync( base::Bind(&DeviceIDFetcher::ComputeOnUIThread, this, salt)); #endif } void DeviceIDFetcher::ComputeOnUIThread(const std::string& salt, const std::string& machine_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (machine_id.empty()) { LOG(ERROR) << "Empty machine id"; RunCallbackOnIOThread(std::string(), PP_ERROR_FAILED); return; } // Build the identifier as follows: // SHA256(machine-id||service||SHA256(machine-id||service||salt)) std::vector salt_bytes; if (!base::HexStringToBytes(salt, &salt_bytes)) salt_bytes.clear(); if (salt_bytes.size() != kSaltLength) { LOG(ERROR) << "Unexpected salt bytes length: " << salt_bytes.size(); RunCallbackOnIOThread(std::string(), PP_ERROR_FAILED); return; } char id_buf[256 / 8]; // 256-bits for SHA256 std::string input = machine_id; input.append(kDRMIdentifierFile); input.append(salt_bytes.begin(), salt_bytes.end()); crypto::SHA256HashString(input, &id_buf, sizeof(id_buf)); std::string id = base::ToLowerASCII( base::HexEncode(reinterpret_cast(id_buf), sizeof(id_buf))); input = machine_id; input.append(kDRMIdentifierFile); input.append(id); crypto::SHA256HashString(input, &id_buf, sizeof(id_buf)); id = base::ToLowerASCII( base::HexEncode(reinterpret_cast(id_buf), sizeof(id_buf))); RunCallbackOnIOThread(id, PP_OK); } // TODO(raymes): This is temporary code to migrate ChromeOS devices to the new // scheme for generating device IDs. Delete this once we are sure most ChromeOS // devices have been migrated. void DeviceIDFetcher::LegacyComputeOnBlockingPool( const base::FilePath& profile_path, const std::string& salt) { std::string id; // First check if the legacy device ID file exists on ChromeOS. If it does, we // should just return that. base::FilePath id_path = GetLegacyDeviceIDPath(profile_path); if (base::PathExists(id_path)) { if (base::ReadFileToString(id_path, &id) && !id.empty()) { RunCallbackOnIOThread(id, PP_OK); return; } } // If we didn't find an ID, get the machine ID and call the new code path to // generate an ID. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&GetMachineIDAsync, base::Bind(&DeviceIDFetcher::ComputeOnUIThread, this, salt))); } void DeviceIDFetcher::RunCallbackOnIOThread(const std::string& id, int32_t result) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&DeviceIDFetcher::RunCallbackOnIOThread, this, id, result)); return; } in_progress_ = false; callback_.Run(id, result); } } // namespace chrome