Home | History | Annotate | Download | only in risk
      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