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/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