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