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