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.
GetTimezoneOffset()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".
GetOperatingSystemVersion()70 std::string GetOperatingSystemVersion() {
71 return base::SysInfo::OperatingSystemName() + " " +
72 base::SysInfo::OperatingSystemVersion();
73 }
74
75 // Adds the list of |fonts| to the |machine|.
AddFontsToFingerprint(const base::ListValue & fonts,Fingerprint::MachineCharacteristics * 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|.
AddPluginsToFingerprint(const std::vector<content::WebPluginInfo> & plugins,Fingerprint::MachineCharacteristics * 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|.
AddAcceptLanguagesToFingerprint(const std::string & accept_languages_str,Fingerprint::MachineCharacteristics * 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|.
AddScreenInfoToFingerprint(const WebScreenInfo & screen_info,Fingerprint::MachineCharacteristics * 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|.
AddCpuInfoToFingerprint(Fingerprint::MachineCharacteristics * 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|.
AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics * machine,const content::GpuDataManager & gpu_data_manager)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:
~FingerprintDataLoader()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
FingerprintDataLoader(uint64 obfuscated_gaia_id,const gfx::Rect & window_bounds,const gfx::Rect & content_bounds,const WebScreenInfo & screen_info,const std::string & version,const std::string & charset,const std::string & accept_languages,const base::Time & install_time,const std::string & app_locale,const std::string & user_agent,const base::TimeDelta & timeout,const base::Callback<void (scoped_ptr<Fingerprint>)> & callback)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
OnGpuInfoUpdate()327 void FingerprintDataLoader::OnGpuInfoUpdate() {
328 if (!gpu_data_manager_->IsEssentialGpuInfoAvailable())
329 return;
330
331 gpu_observer_.Remove(gpu_data_manager_);
332 MaybeFillFingerprint();
333 }
334
OnGotFonts(scoped_ptr<base::ListValue> fonts)335 void FingerprintDataLoader::OnGotFonts(scoped_ptr<base::ListValue> fonts) {
336 DCHECK(!fonts_);
337 fonts_.reset(fonts.release());
338 MaybeFillFingerprint();
339 }
340
OnGotPlugins(const std::vector<content::WebPluginInfo> & plugins)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
OnGotGeoposition(const content::Geoposition & geoposition)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
MaybeFillFingerprint()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
FillFingerprint()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
GetFingerprintInternal(uint64 obfuscated_gaia_id,const gfx::Rect & window_bounds,const gfx::Rect & content_bounds,const blink::WebScreenInfo & screen_info,const std::string & version,const std::string & charset,const std::string & accept_languages,const base::Time & install_time,const std::string & app_locale,const std::string & user_agent,const base::TimeDelta & timeout,const base::Callback<void (scoped_ptr<Fingerprint>)> & callback)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
GetFingerprint(uint64 obfuscated_gaia_id,const gfx::Rect & window_bounds,content::WebContents * web_contents,const std::string & version,const std::string & charset,const std::string & accept_languages,const base::Time & install_time,const std::string & app_locale,const std::string & user_agent,const base::Callback<void (scoped_ptr<Fingerprint>)> & callback)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