1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 // 5 // Generating a fingerprint consists of two major steps: 6 // (1) Gather all the necessary data. 7 // (2) Write it into a protocol buffer. 8 // 9 // Step (2) is as simple as it sounds -- it's really just a matter of copying 10 // data. Step (1) requires waiting on several asynchronous callbacks, which are 11 // managed by the FingerprintDataLoader class. 12 13 #include "components/autofill/content/browser/risk/fingerprint.h" 14 15 #include "base/bind.h" 16 #include "base/callback.h" 17 #include "base/cpu.h" 18 #include "base/logging.h" 19 #include "base/memory/weak_ptr.h" 20 #include "base/scoped_observer.h" 21 #include "base/strings/string_split.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "base/sys_info.h" 24 #include "base/time/time.h" 25 #include "base/timer/timer.h" 26 #include "base/values.h" 27 #include "components/autofill/content/browser/risk/proto/fingerprint.pb.h" 28 #include "content/public/browser/browser_thread.h" 29 #include "content/public/browser/font_list_async.h" 30 #include "content/public/browser/geolocation_provider.h" 31 #include "content/public/browser/gpu_data_manager.h" 32 #include "content/public/browser/gpu_data_manager_observer.h" 33 #include "content/public/browser/plugin_service.h" 34 #include "content/public/browser/render_widget_host.h" 35 #include "content/public/browser/render_widget_host_view.h" 36 #include "content/public/browser/web_contents.h" 37 #include "content/public/common/geoposition.h" 38 #include "content/public/common/webplugininfo.h" 39 #include "gpu/config/gpu_info.h" 40 #include "third_party/WebKit/public/platform/WebRect.h" 41 #include "third_party/WebKit/public/platform/WebScreenInfo.h" 42 #include "ui/gfx/rect.h" 43 #include "ui/gfx/screen.h" 44 45 using blink::WebScreenInfo; 46 47 namespace autofill { 48 namespace risk { 49 50 namespace { 51 52 const int32 kFingerprinterVersion = 1; 53 54 // Maximum amount of time, in seconds, to wait for loading asynchronous 55 // fingerprint data. 56 const int kTimeoutSeconds = 4; 57 58 // Returns the delta between the local timezone and UTC. 59 base::TimeDelta GetTimezoneOffset() { 60 const base::Time utc = base::Time::Now(); 61 62 base::Time::Exploded local; 63 utc.LocalExplode(&local); 64 65 return base::Time::FromUTCExploded(local) - utc; 66 } 67 68 // Returns the concatenation of the operating system name and version, e.g. 69 // "Mac OS X 10.6.8". 70 std::string GetOperatingSystemVersion() { 71 return base::SysInfo::OperatingSystemName() + " " + 72 base::SysInfo::OperatingSystemVersion(); 73 } 74 75 // Adds the list of |fonts| to the |machine|. 76 void AddFontsToFingerprint(const base::ListValue& fonts, 77 Fingerprint::MachineCharacteristics* machine) { 78 for (base::ListValue::const_iterator it = fonts.begin(); 79 it != fonts.end(); ++it) { 80 // Each item in the list is a two-element list such that the first element 81 // is the font family and the second is the font name. 82 const base::ListValue* font_description = NULL; 83 bool success = (*it)->GetAsList(&font_description); 84 DCHECK(success); 85 86 std::string font_name; 87 success = font_description->GetString(1, &font_name); 88 DCHECK(success); 89 90 machine->add_font(font_name); 91 } 92 } 93 94 // Adds the list of |plugins| to the |machine|. 95 void AddPluginsToFingerprint(const std::vector<content::WebPluginInfo>& plugins, 96 Fingerprint::MachineCharacteristics* machine) { 97 for (std::vector<content::WebPluginInfo>::const_iterator it = plugins.begin(); 98 it != plugins.end(); ++it) { 99 Fingerprint::MachineCharacteristics::Plugin* plugin = 100 machine->add_plugin(); 101 plugin->set_name(base::UTF16ToUTF8(it->name)); 102 plugin->set_description(base::UTF16ToUTF8(it->desc)); 103 for (std::vector<content::WebPluginMimeType>::const_iterator mime_type = 104 it->mime_types.begin(); 105 mime_type != it->mime_types.end(); ++mime_type) { 106 plugin->add_mime_type(mime_type->mime_type); 107 } 108 plugin->set_version(base::UTF16ToUTF8(it->version)); 109 } 110 } 111 112 // Adds the list of HTTP accept languages to the |machine|. 113 void AddAcceptLanguagesToFingerprint( 114 const std::string& accept_languages_str, 115 Fingerprint::MachineCharacteristics* machine) { 116 std::vector<std::string> accept_languages; 117 base::SplitString(accept_languages_str, ',', &accept_languages); 118 for (std::vector<std::string>::const_iterator it = accept_languages.begin(); 119 it != accept_languages.end(); ++it) { 120 machine->add_requested_language(*it); 121 } 122 } 123 124 // This function writes 125 // (a) the number of screens, 126 // (b) the primary display's screen size, 127 // (c) the screen's color depth, and 128 // (d) the size of the screen unavailable to web page content, 129 // i.e. the Taskbar size on Windows 130 // into the |machine|. 131 void AddScreenInfoToFingerprint(const WebScreenInfo& screen_info, 132 Fingerprint::MachineCharacteristics* machine) { 133 // TODO(scottmg): NativeScreen maybe wrong. http://crbug.com/133312 134 machine->set_screen_count( 135 gfx::Screen::GetNativeScreen()->GetNumDisplays()); 136 137 const gfx::Size screen_size = 138 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().GetSizeInPixel(); 139 machine->mutable_screen_size()->set_width(screen_size.width()); 140 machine->mutable_screen_size()->set_height(screen_size.height()); 141 142 machine->set_screen_color_depth(screen_info.depth); 143 144 const gfx::Rect screen_rect(screen_info.rect); 145 const gfx::Rect available_rect(screen_info.availableRect); 146 const gfx::Rect unavailable_rect = 147 gfx::SubtractRects(screen_rect, available_rect); 148 machine->mutable_unavailable_screen_size()->set_width( 149 unavailable_rect.width()); 150 machine->mutable_unavailable_screen_size()->set_height( 151 unavailable_rect.height()); 152 } 153 154 // Writes info about the machine's CPU into the |machine|. 155 void AddCpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine) { 156 base::CPU cpu; 157 machine->mutable_cpu()->set_vendor_name(cpu.vendor_name()); 158 machine->mutable_cpu()->set_brand(cpu.cpu_brand()); 159 } 160 161 // Writes info about the machine's GPU into the |machine|. 162 void AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine, 163 const content::GpuDataManager& gpu_data_manager) { 164 if (!gpu_data_manager.IsEssentialGpuInfoAvailable()) 165 return; 166 167 const gpu::GPUInfo gpu_info = gpu_data_manager.GetGPUInfo(); 168 169 Fingerprint::MachineCharacteristics::Graphics* graphics = 170 machine->mutable_graphics_card(); 171 graphics->set_vendor_id(gpu_info.gpu.vendor_id); 172 graphics->set_device_id(gpu_info.gpu.device_id); 173 graphics->set_driver_version(gpu_info.driver_version); 174 graphics->set_driver_date(gpu_info.driver_date); 175 176 Fingerprint::MachineCharacteristics::Graphics::PerformanceStatistics* 177 gpu_performance = graphics->mutable_performance_statistics(); 178 gpu_performance->set_graphics_score(gpu_info.performance_stats.graphics); 179 gpu_performance->set_gaming_score(gpu_info.performance_stats.gaming); 180 gpu_performance->set_overall_score(gpu_info.performance_stats.overall); 181 } 182 183 // Waits for all asynchronous data required for the fingerprint to be loaded, 184 // then fills out the fingerprint. 185 class FingerprintDataLoader : public content::GpuDataManagerObserver { 186 public: 187 FingerprintDataLoader( 188 uint64 obfuscated_gaia_id, 189 const gfx::Rect& window_bounds, 190 const gfx::Rect& content_bounds, 191 const WebScreenInfo& screen_info, 192 const std::string& version, 193 const std::string& charset, 194 const std::string& accept_languages, 195 const base::Time& install_time, 196 const std::string& app_locale, 197 const std::string& user_agent, 198 const base::TimeDelta& timeout, 199 const base::Callback<void(scoped_ptr<Fingerprint>)>& callback); 200 201 private: 202 virtual ~FingerprintDataLoader() {} 203 204 // content::GpuDataManagerObserver: 205 virtual void OnGpuInfoUpdate() OVERRIDE; 206 207 // Callbacks for asynchronously loaded data. 208 void OnGotFonts(scoped_ptr<base::ListValue> fonts); 209 void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins); 210 void OnGotGeoposition(const content::Geoposition& geoposition); 211 212 // If all of the asynchronous data has been loaded, calls |callback_| with 213 // the fingerprint data. 214 void MaybeFillFingerprint(); 215 216 // Calls |callback_| with the fingerprint data. 217 void FillFingerprint(); 218 219 // The GPU data provider. 220 // Weak reference because the GpuDataManager class is a singleton. 221 content::GpuDataManager* const gpu_data_manager_; 222 223 // Ensures that any observer registrations for the GPU data are cleaned up by 224 // the time this object is destroyed. 225 ScopedObserver<content::GpuDataManager, FingerprintDataLoader> gpu_observer_; 226 227 // Data that will be passed on to the next loading phase. See the comment for 228 // GetFingerprint() for a description of these variables. 229 const uint64 obfuscated_gaia_id_; 230 const gfx::Rect window_bounds_; 231 const gfx::Rect content_bounds_; 232 const WebScreenInfo screen_info_; 233 const std::string version_; 234 const std::string charset_; 235 const std::string accept_languages_; 236 const std::string app_locale_; 237 const std::string user_agent_; 238 const base::Time install_time_; 239 240 // Data that will be loaded asynchronously. 241 scoped_ptr<base::ListValue> fonts_; 242 std::vector<content::WebPluginInfo> plugins_; 243 bool waiting_on_plugins_; 244 content::Geoposition geoposition_; 245 246 // Timer to enforce a maximum timeout before the |callback_| is called, even 247 // if not all asynchronous data has been loaded. 248 base::OneShotTimer<FingerprintDataLoader> timeout_timer_; 249 250 // For invalidating asynchronous callbacks that might arrive after |this| 251 // instance is destroyed. 252 base::WeakPtrFactory<FingerprintDataLoader> weak_ptr_factory_; 253 254 // The callback that will be called once all the data is available. 255 base::Callback<void(scoped_ptr<Fingerprint>)> callback_; 256 257 // The callback used as an "observer" of the GeolocationProvider. 258 scoped_ptr<content::GeolocationProvider::Subscription> 259 geolocation_subscription_; 260 261 DISALLOW_COPY_AND_ASSIGN(FingerprintDataLoader); 262 }; 263 264 FingerprintDataLoader::FingerprintDataLoader( 265 uint64 obfuscated_gaia_id, 266 const gfx::Rect& window_bounds, 267 const gfx::Rect& content_bounds, 268 const WebScreenInfo& screen_info, 269 const std::string& version, 270 const std::string& charset, 271 const std::string& accept_languages, 272 const base::Time& install_time, 273 const std::string& app_locale, 274 const std::string& user_agent, 275 const base::TimeDelta& timeout, 276 const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) 277 : gpu_data_manager_(content::GpuDataManager::GetInstance()), 278 gpu_observer_(this), 279 obfuscated_gaia_id_(obfuscated_gaia_id), 280 window_bounds_(window_bounds), 281 content_bounds_(content_bounds), 282 screen_info_(screen_info), 283 version_(version), 284 charset_(charset), 285 accept_languages_(accept_languages), 286 app_locale_(app_locale), 287 user_agent_(user_agent), 288 install_time_(install_time), 289 waiting_on_plugins_(true), 290 weak_ptr_factory_(this), 291 callback_(callback) { 292 DCHECK(!install_time_.is_null()); 293 294 timeout_timer_.Start(FROM_HERE, timeout, 295 base::Bind(&FingerprintDataLoader::MaybeFillFingerprint, 296 weak_ptr_factory_.GetWeakPtr())); 297 298 // Load GPU data if needed. 299 if (gpu_data_manager_->GpuAccessAllowed(NULL) && 300 !gpu_data_manager_->IsEssentialGpuInfoAvailable()) { 301 gpu_observer_.Add(gpu_data_manager_); 302 gpu_data_manager_->RequestCompleteGpuInfoIfNeeded(); 303 } 304 305 #if defined(ENABLE_PLUGINS) 306 // Load plugin data. 307 content::PluginService::GetInstance()->GetPlugins( 308 base::Bind(&FingerprintDataLoader::OnGotPlugins, 309 weak_ptr_factory_.GetWeakPtr())); 310 #else 311 waiting_on_plugins_ = false; 312 #endif 313 314 // Load font data. 315 content::GetFontListAsync( 316 base::Bind(&FingerprintDataLoader::OnGotFonts, 317 weak_ptr_factory_.GetWeakPtr())); 318 319 // Load geolocation data. 320 geolocation_subscription_ = content::GeolocationProvider::GetInstance()-> 321 AddLocationUpdateCallback( 322 base::Bind(&FingerprintDataLoader::OnGotGeoposition, 323 weak_ptr_factory_.GetWeakPtr()), 324 false); 325 } 326 327 void FingerprintDataLoader::OnGpuInfoUpdate() { 328 if (!gpu_data_manager_->IsEssentialGpuInfoAvailable()) 329 return; 330 331 gpu_observer_.Remove(gpu_data_manager_); 332 MaybeFillFingerprint(); 333 } 334 335 void FingerprintDataLoader::OnGotFonts(scoped_ptr<base::ListValue> fonts) { 336 DCHECK(!fonts_); 337 fonts_.reset(fonts.release()); 338 MaybeFillFingerprint(); 339 } 340 341 void FingerprintDataLoader::OnGotPlugins( 342 const std::vector<content::WebPluginInfo>& plugins) { 343 DCHECK(waiting_on_plugins_); 344 waiting_on_plugins_ = false; 345 plugins_ = plugins; 346 MaybeFillFingerprint(); 347 } 348 349 void FingerprintDataLoader::OnGotGeoposition( 350 const content::Geoposition& geoposition) { 351 DCHECK(!geoposition_.Validate()); 352 353 geoposition_ = geoposition; 354 DCHECK(geoposition_.Validate() || 355 geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE); 356 geolocation_subscription_.reset(); 357 358 MaybeFillFingerprint(); 359 } 360 361 void FingerprintDataLoader::MaybeFillFingerprint() { 362 // If all of the data has been loaded, or if the |timeout_timer_| has expired, 363 // fill the fingerprint and clean up. 364 if (!timeout_timer_.IsRunning() || 365 ((!gpu_data_manager_->GpuAccessAllowed(NULL) || 366 gpu_data_manager_->IsEssentialGpuInfoAvailable()) && 367 fonts_ && 368 !waiting_on_plugins_ && 369 (geoposition_.Validate() || 370 geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE))) { 371 FillFingerprint(); 372 delete this; 373 } 374 } 375 376 void FingerprintDataLoader::FillFingerprint() { 377 scoped_ptr<Fingerprint> fingerprint(new Fingerprint); 378 Fingerprint::MachineCharacteristics* machine = 379 fingerprint->mutable_machine_characteristics(); 380 381 machine->set_operating_system_build(GetOperatingSystemVersion()); 382 // We use the delta between the install time and the Unix epoch, in hours. 383 machine->set_browser_install_time_hours( 384 (install_time_ - base::Time::UnixEpoch()).InHours()); 385 machine->set_utc_offset_ms(GetTimezoneOffset().InMilliseconds()); 386 machine->set_browser_language(app_locale_); 387 machine->set_charset(charset_); 388 machine->set_user_agent(user_agent_); 389 machine->set_ram(base::SysInfo::AmountOfPhysicalMemory()); 390 machine->set_browser_build(version_); 391 machine->set_browser_feature( 392 Fingerprint::MachineCharacteristics::FEATURE_REQUEST_AUTOCOMPLETE); 393 if (fonts_) 394 AddFontsToFingerprint(*fonts_, machine); 395 AddPluginsToFingerprint(plugins_, machine); 396 AddAcceptLanguagesToFingerprint(accept_languages_, machine); 397 AddScreenInfoToFingerprint(screen_info_, machine); 398 AddCpuInfoToFingerprint(machine); 399 AddGpuInfoToFingerprint(machine, *gpu_data_manager_); 400 401 // TODO(isherman): Record the user_and_device_name_hash. 402 // TODO(isherman): Record the partition size of the hard drives? 403 404 Fingerprint::TransientState* transient_state = 405 fingerprint->mutable_transient_state(); 406 Fingerprint::Dimension* inner_window_size = 407 transient_state->mutable_inner_window_size(); 408 inner_window_size->set_width(content_bounds_.width()); 409 inner_window_size->set_height(content_bounds_.height()); 410 Fingerprint::Dimension* outer_window_size = 411 transient_state->mutable_outer_window_size(); 412 outer_window_size->set_width(window_bounds_.width()); 413 outer_window_size->set_height(window_bounds_.height()); 414 415 // TODO(isherman): Record network performance data, which is theoretically 416 // available to JS. 417 418 // TODO(isherman): Record more user behavior data. 419 if (geoposition_.Validate() && 420 geoposition_.error_code == content::Geoposition::ERROR_CODE_NONE) { 421 Fingerprint::UserCharacteristics::Location* location = 422 fingerprint->mutable_user_characteristics()->mutable_location(); 423 location->set_altitude(geoposition_.altitude); 424 location->set_latitude(geoposition_.latitude); 425 location->set_longitude(geoposition_.longitude); 426 location->set_accuracy(geoposition_.accuracy); 427 location->set_time_in_ms( 428 (geoposition_.timestamp - base::Time::UnixEpoch()).InMilliseconds()); 429 } 430 431 Fingerprint::Metadata* metadata = fingerprint->mutable_metadata(); 432 metadata->set_timestamp_ms( 433 (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds()); 434 metadata->set_obfuscated_gaia_id(obfuscated_gaia_id_); 435 metadata->set_fingerprinter_version(kFingerprinterVersion); 436 437 callback_.Run(fingerprint.Pass()); 438 } 439 440 } // namespace 441 442 namespace internal { 443 444 void GetFingerprintInternal( 445 uint64 obfuscated_gaia_id, 446 const gfx::Rect& window_bounds, 447 const gfx::Rect& content_bounds, 448 const blink::WebScreenInfo& screen_info, 449 const std::string& version, 450 const std::string& charset, 451 const std::string& accept_languages, 452 const base::Time& install_time, 453 const std::string& app_locale, 454 const std::string& user_agent, 455 const base::TimeDelta& timeout, 456 const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) { 457 // Begin loading all of the data that we need to load asynchronously. 458 // This class is responsible for freeing its own memory. 459 new FingerprintDataLoader(obfuscated_gaia_id, window_bounds, content_bounds, 460 screen_info, version, charset, accept_languages, 461 install_time, app_locale, user_agent, timeout, 462 callback); 463 } 464 465 } // namespace internal 466 467 void GetFingerprint( 468 uint64 obfuscated_gaia_id, 469 const gfx::Rect& window_bounds, 470 content::WebContents* web_contents, 471 const std::string& version, 472 const std::string& charset, 473 const std::string& accept_languages, 474 const base::Time& install_time, 475 const std::string& app_locale, 476 const std::string& user_agent, 477 const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) { 478 gfx::Rect content_bounds = web_contents->GetContainerBounds(); 479 480 blink::WebScreenInfo screen_info; 481 content::RenderWidgetHostView* host_view = 482 web_contents->GetRenderWidgetHostView(); 483 if (host_view) 484 host_view->GetRenderWidgetHost()->GetWebScreenInfo(&screen_info); 485 486 internal::GetFingerprintInternal( 487 obfuscated_gaia_id, window_bounds, content_bounds, screen_info, version, 488 charset, accept_languages, install_time, app_locale, user_agent, 489 base::TimeDelta::FromSeconds(kTimeoutSeconds), callback); 490 } 491 492 } // namespace risk 493 } // namespace autofill 494