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