toolkit/components/downloads/ApplicationReputation.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 // See
michael@0 7 // https://wiki.mozilla.org/Security/Features/Application_Reputation_Design_Doc
michael@0 8 // for a description of Chrome's implementation of this feature.
michael@0 9 #include "ApplicationReputation.h"
michael@0 10 #include "csd.pb.h"
michael@0 11
michael@0 12 #include "nsIArray.h"
michael@0 13 #include "nsIApplicationReputation.h"
michael@0 14 #include "nsIChannel.h"
michael@0 15 #include "nsIHttpChannel.h"
michael@0 16 #include "nsIIOService.h"
michael@0 17 #include "nsIPrefService.h"
michael@0 18 #include "nsIScriptSecurityManager.h"
michael@0 19 #include "nsIStreamListener.h"
michael@0 20 #include "nsIStringStream.h"
michael@0 21 #include "nsIUploadChannel2.h"
michael@0 22 #include "nsIURI.h"
michael@0 23 #include "nsIUrlClassifierDBService.h"
michael@0 24 #include "nsIX509Cert.h"
michael@0 25 #include "nsIX509CertDB.h"
michael@0 26 #include "nsIX509CertList.h"
michael@0 27
michael@0 28 #include "mozilla/Preferences.h"
michael@0 29 #include "mozilla/Services.h"
michael@0 30 #include "mozilla/Telemetry.h"
michael@0 31 #include "mozilla/TimeStamp.h"
michael@0 32 #include "mozilla/LoadContext.h"
michael@0 33
michael@0 34 #include "nsAutoPtr.h"
michael@0 35 #include "nsCOMPtr.h"
michael@0 36 #include "nsDebug.h"
michael@0 37 #include "nsError.h"
michael@0 38 #include "nsNetCID.h"
michael@0 39 #include "nsReadableUtils.h"
michael@0 40 #include "nsServiceManagerUtils.h"
michael@0 41 #include "nsString.h"
michael@0 42 #include "nsTArray.h"
michael@0 43 #include "nsThreadUtils.h"
michael@0 44 #include "nsXPCOMStrings.h"
michael@0 45
michael@0 46 using mozilla::Preferences;
michael@0 47 using mozilla::TimeStamp;
michael@0 48 using mozilla::Telemetry::Accumulate;
michael@0 49 using safe_browsing::ClientDownloadRequest;
michael@0 50 using safe_browsing::ClientDownloadRequest_SignatureInfo;
michael@0 51 using safe_browsing::ClientDownloadRequest_CertificateChain;
michael@0 52
michael@0 53 // Preferences that we need to initialize the query.
michael@0 54 #define PREF_SB_APP_REP_URL "browser.safebrowsing.appRepURL"
michael@0 55 #define PREF_SB_MALWARE_ENABLED "browser.safebrowsing.malware.enabled"
michael@0 56 #define PREF_GENERAL_LOCALE "general.useragent.locale"
michael@0 57 #define PREF_DOWNLOAD_BLOCK_TABLE "urlclassifier.downloadBlockTable"
michael@0 58 #define PREF_DOWNLOAD_ALLOW_TABLE "urlclassifier.downloadAllowTable"
michael@0 59
michael@0 60 // NSPR_LOG_MODULES=ApplicationReputation:5
michael@0 61 #if defined(PR_LOGGING)
michael@0 62 PRLogModuleInfo *ApplicationReputationService::prlog = nullptr;
michael@0 63 #define LOG(args) PR_LOG(ApplicationReputationService::prlog, PR_LOG_DEBUG, args)
michael@0 64 #define LOG_ENABLED() PR_LOG_TEST(ApplicationReputationService::prlog, 4)
michael@0 65 #else
michael@0 66 #define LOG(args)
michael@0 67 #define LOG_ENABLED() (false)
michael@0 68 #endif
michael@0 69
michael@0 70 class PendingDBLookup;
michael@0 71
michael@0 72 // A single use class private to ApplicationReputationService encapsulating an
michael@0 73 // nsIApplicationReputationQuery and an nsIApplicationReputationCallback. Once
michael@0 74 // created by ApplicationReputationService, it is guaranteed to call mCallback.
michael@0 75 // This class is private to ApplicationReputationService.
michael@0 76 class PendingLookup MOZ_FINAL : public nsIStreamListener
michael@0 77 {
michael@0 78 public:
michael@0 79 NS_DECL_ISUPPORTS
michael@0 80 NS_DECL_NSIREQUESTOBSERVER
michael@0 81 NS_DECL_NSISTREAMLISTENER
michael@0 82
michael@0 83 // Constructor and destructor.
michael@0 84 PendingLookup(nsIApplicationReputationQuery* aQuery,
michael@0 85 nsIApplicationReputationCallback* aCallback);
michael@0 86 ~PendingLookup();
michael@0 87
michael@0 88 // Start the lookup. The lookup may have 2 parts: local and remote. In the
michael@0 89 // local lookup, PendingDBLookups are created to query the local allow and
michael@0 90 // blocklists for various URIs associated with this downloaded file. In the
michael@0 91 // event that no results are found, a remote lookup is sent to the Application
michael@0 92 // Reputation server.
michael@0 93 nsresult StartLookup();
michael@0 94
michael@0 95 private:
michael@0 96 friend class PendingDBLookup;
michael@0 97
michael@0 98 // Telemetry states.
michael@0 99 // Status of the remote response (valid or not).
michael@0 100 enum SERVER_RESPONSE_TYPES {
michael@0 101 SERVER_RESPONSE_VALID = 0,
michael@0 102 SERVER_RESPONSE_FAILED = 1,
michael@0 103 SERVER_RESPONSE_INVALID = 2,
michael@0 104 };
michael@0 105
michael@0 106 // The query containing metadata about the downloaded file.
michael@0 107 nsCOMPtr<nsIApplicationReputationQuery> mQuery;
michael@0 108
michael@0 109 // The callback with which to report the verdict.
michael@0 110 nsCOMPtr<nsIApplicationReputationCallback> mCallback;
michael@0 111
michael@0 112 // An array of strings created from certificate information used to whitelist
michael@0 113 // the downloaded file.
michael@0 114 nsTArray<nsCString> mAllowlistSpecs;
michael@0 115 // The source URI of the download, the referrer and possibly any redirects.
michael@0 116 nsTArray<nsCString> mAnylistSpecs;
michael@0 117
michael@0 118 // When we started this query
michael@0 119 TimeStamp mStartTime;
michael@0 120
michael@0 121 // The protocol buffer used to store signature information extracted using
michael@0 122 // the Windows Authenticode API, if the binary is signed.
michael@0 123 ClientDownloadRequest_SignatureInfo mSignatureInfo;
michael@0 124
michael@0 125 // The response from the application reputation query. This is read in chunks
michael@0 126 // as part of our nsIStreamListener implementation and may contain embedded
michael@0 127 // NULLs.
michael@0 128 nsCString mResponse;
michael@0 129
michael@0 130 // Returns true if the file is likely to be binary on Windows.
michael@0 131 bool IsBinaryFile();
michael@0 132
michael@0 133 // Clean up and call the callback. PendingLookup must not be used after this
michael@0 134 // function is called.
michael@0 135 nsresult OnComplete(bool shouldBlock, nsresult rv);
michael@0 136
michael@0 137 // Wrapper function for nsIStreamListener.onStopRequest to make it easy to
michael@0 138 // guarantee calling the callback
michael@0 139 nsresult OnStopRequestInternal(nsIRequest *aRequest,
michael@0 140 nsISupports *aContext,
michael@0 141 nsresult aResult,
michael@0 142 bool* aShouldBlock);
michael@0 143
michael@0 144 // Escape '/' and '%' in certificate attribute values.
michael@0 145 nsCString EscapeCertificateAttribute(const nsACString& aAttribute);
michael@0 146
michael@0 147 // Escape ':' in fingerprint values.
michael@0 148 nsCString EscapeFingerprint(const nsACString& aAttribute);
michael@0 149
michael@0 150 // Generate whitelist strings for the given certificate pair from the same
michael@0 151 // certificate chain.
michael@0 152 nsresult GenerateWhitelistStringsForPair(
michael@0 153 nsIX509Cert* certificate, nsIX509Cert* issuer);
michael@0 154
michael@0 155 // Generate whitelist strings for the given certificate chain, which starts
michael@0 156 // with the signer and may go all the way to the root cert.
michael@0 157 nsresult GenerateWhitelistStringsForChain(
michael@0 158 const ClientDownloadRequest_CertificateChain& aChain);
michael@0 159
michael@0 160 // For signed binaries, generate strings of the form:
michael@0 161 // http://sb-ssl.google.com/safebrowsing/csd/certificate/
michael@0 162 // <issuer_cert_sha1_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
michael@0 163 // for each (cert, issuer) pair in each chain of certificates that is
michael@0 164 // associated with the binary.
michael@0 165 nsresult GenerateWhitelistStrings(
michael@0 166 const ClientDownloadRequest_SignatureInfo& aSignatureInfo);
michael@0 167
michael@0 168 // Parse the XPCOM certificate lists and stick them into the protocol buffer
michael@0 169 // version.
michael@0 170 nsresult ParseCertificates(nsIArray* aSigArray,
michael@0 171 ClientDownloadRequest_SignatureInfo* aSigInfo);
michael@0 172
michael@0 173 // Helper function to ensure that we call PendingLookup::LookupNext or
michael@0 174 // PendingLookup::OnComplete.
michael@0 175 nsresult DoLookupInternal();
michael@0 176
michael@0 177 // Looks up all the URIs that may be responsible for allowlisting or
michael@0 178 // blocklisting the downloaded file. These URIs may include whitelist strings
michael@0 179 // generated by certificates verifying the binary as well as the target URI
michael@0 180 // from which the file was downloaded.
michael@0 181 nsresult LookupNext();
michael@0 182
michael@0 183 // Sends a query to the remote application reputation service. Returns NS_OK
michael@0 184 // on success.
michael@0 185 nsresult SendRemoteQuery();
michael@0 186
michael@0 187 // Helper function to ensure that we always call the callback.
michael@0 188 nsresult SendRemoteQueryInternal();
michael@0 189 };
michael@0 190
michael@0 191 // A single-use class for looking up a single URI in the safebrowsing DB. This
michael@0 192 // class is private to PendingLookup.
michael@0 193 class PendingDBLookup MOZ_FINAL : public nsIUrlClassifierCallback
michael@0 194 {
michael@0 195 public:
michael@0 196 NS_DECL_ISUPPORTS
michael@0 197 NS_DECL_NSIURLCLASSIFIERCALLBACK
michael@0 198
michael@0 199 // Constructor and destructor
michael@0 200 PendingDBLookup(PendingLookup* aPendingLookup);
michael@0 201 ~PendingDBLookup();
michael@0 202
michael@0 203 // Look up the given URI in the safebrowsing DBs, optionally on both the allow
michael@0 204 // list and the blocklist. If there is a match, call
michael@0 205 // PendingLookup::OnComplete. Otherwise, call PendingLookup::LookupNext.
michael@0 206 nsresult LookupSpec(const nsACString& aSpec, bool aAllowlistOnly);
michael@0 207 private:
michael@0 208 // The download appeared on the allowlist, blocklist, or no list (and thus
michael@0 209 // could trigger a remote query.
michael@0 210 enum LIST_TYPES {
michael@0 211 ALLOW_LIST = 0,
michael@0 212 BLOCK_LIST = 1,
michael@0 213 NO_LIST = 2,
michael@0 214 };
michael@0 215
michael@0 216 nsCString mSpec;
michael@0 217 bool mAllowlistOnly;
michael@0 218 nsRefPtr<PendingLookup> mPendingLookup;
michael@0 219 nsresult LookupSpecInternal(const nsACString& aSpec);
michael@0 220 };
michael@0 221
michael@0 222 NS_IMPL_ISUPPORTS(PendingDBLookup,
michael@0 223 nsIUrlClassifierCallback)
michael@0 224
michael@0 225 PendingDBLookup::PendingDBLookup(PendingLookup* aPendingLookup) :
michael@0 226 mAllowlistOnly(false),
michael@0 227 mPendingLookup(aPendingLookup)
michael@0 228 {
michael@0 229 LOG(("Created pending DB lookup [this = %p]", this));
michael@0 230 }
michael@0 231
michael@0 232 PendingDBLookup::~PendingDBLookup()
michael@0 233 {
michael@0 234 LOG(("Destroying pending DB lookup [this = %p]", this));
michael@0 235 mPendingLookup = nullptr;
michael@0 236 }
michael@0 237
michael@0 238 nsresult
michael@0 239 PendingDBLookup::LookupSpec(const nsACString& aSpec,
michael@0 240 bool aAllowlistOnly)
michael@0 241 {
michael@0 242 LOG(("Checking principal %s", aSpec.Data()));
michael@0 243 mSpec = aSpec;
michael@0 244 mAllowlistOnly = aAllowlistOnly;
michael@0 245 nsresult rv = LookupSpecInternal(aSpec);
michael@0 246 if (NS_FAILED(rv)) {
michael@0 247 LOG(("Error in LookupSpecInternal"));
michael@0 248 return mPendingLookup->OnComplete(false, NS_OK);
michael@0 249 }
michael@0 250 // LookupSpecInternal has called nsIUrlClassifierCallback.lookup, which is
michael@0 251 // guaranteed to call HandleEvent.
michael@0 252 return rv;
michael@0 253 }
michael@0 254
michael@0 255 nsresult
michael@0 256 PendingDBLookup::LookupSpecInternal(const nsACString& aSpec)
michael@0 257 {
michael@0 258 nsresult rv;
michael@0 259
michael@0 260 nsCOMPtr<nsIURI> uri;
michael@0 261 nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
michael@0 262 rv = ios->NewURI(aSpec, nullptr, nullptr, getter_AddRefs(uri));
michael@0 263 NS_ENSURE_SUCCESS(rv, rv);
michael@0 264
michael@0 265 nsCOMPtr<nsIPrincipal> principal;
michael@0 266 nsCOMPtr<nsIScriptSecurityManager> secMan =
michael@0 267 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
michael@0 268 NS_ENSURE_SUCCESS(rv, rv);
michael@0 269
michael@0 270 rv = secMan->GetNoAppCodebasePrincipal(uri, getter_AddRefs(principal));
michael@0 271 NS_ENSURE_SUCCESS(rv, rv);
michael@0 272
michael@0 273 // Check local lists to see if the URI has already been whitelisted or
michael@0 274 // blacklisted.
michael@0 275 LOG(("Checking DB service for principal %s [this = %p]", mSpec.get(), this));
michael@0 276 nsCOMPtr<nsIUrlClassifierDBService> dbService =
michael@0 277 do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
michael@0 278 nsAutoCString tables;
michael@0 279 nsAutoCString allowlist;
michael@0 280 Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowlist);
michael@0 281 if (!allowlist.IsEmpty()) {
michael@0 282 tables.Append(allowlist);
michael@0 283 }
michael@0 284 nsAutoCString blocklist;
michael@0 285 Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blocklist);
michael@0 286 if (!mAllowlistOnly && !blocklist.IsEmpty()) {
michael@0 287 tables.Append(",");
michael@0 288 tables.Append(blocklist);
michael@0 289 }
michael@0 290 return dbService->Lookup(principal, tables, this);
michael@0 291 }
michael@0 292
michael@0 293 NS_IMETHODIMP
michael@0 294 PendingDBLookup::HandleEvent(const nsACString& tables)
michael@0 295 {
michael@0 296 // HandleEvent is guaranteed to call either:
michael@0 297 // 1) PendingLookup::OnComplete if the URL can be classified locally, or
michael@0 298 // 2) PendingLookup::LookupNext if the URL can be cannot classified locally.
michael@0 299 // Blocklisting trumps allowlisting.
michael@0 300 nsAutoCString blockList;
michael@0 301 Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
michael@0 302 if (!mAllowlistOnly && FindInReadable(blockList, tables)) {
michael@0 303 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
michael@0 304 LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
michael@0 305 return mPendingLookup->OnComplete(true, NS_OK);
michael@0 306 }
michael@0 307
michael@0 308 nsAutoCString allowList;
michael@0 309 Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
michael@0 310 if (FindInReadable(allowList, tables)) {
michael@0 311 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
michael@0 312 LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
michael@0 313 return mPendingLookup->OnComplete(false, NS_OK);
michael@0 314 }
michael@0 315
michael@0 316 LOG(("Didn't find principal %s on any list [this = %p]", mSpec.get(), this));
michael@0 317 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
michael@0 318 return mPendingLookup->LookupNext();
michael@0 319 }
michael@0 320
michael@0 321 NS_IMPL_ISUPPORTS(PendingLookup,
michael@0 322 nsIStreamListener,
michael@0 323 nsIRequestObserver)
michael@0 324
michael@0 325 PendingLookup::PendingLookup(nsIApplicationReputationQuery* aQuery,
michael@0 326 nsIApplicationReputationCallback* aCallback) :
michael@0 327 mQuery(aQuery),
michael@0 328 mCallback(aCallback)
michael@0 329 {
michael@0 330 LOG(("Created pending lookup [this = %p]", this));
michael@0 331 }
michael@0 332
michael@0 333 PendingLookup::~PendingLookup()
michael@0 334 {
michael@0 335 LOG(("Destroying pending lookup [this = %p]", this));
michael@0 336 }
michael@0 337
michael@0 338 bool
michael@0 339 PendingLookup::IsBinaryFile()
michael@0 340 {
michael@0 341 nsString fileName;
michael@0 342 nsresult rv = mQuery->GetSuggestedFileName(fileName);
michael@0 343 if (NS_FAILED(rv)) {
michael@0 344 return false;
michael@0 345 }
michael@0 346 return
michael@0 347 // Executable extensions for MS Windows, from
michael@0 348 // https://code.google.com/p/chromium/codesearch#chromium/src/chrome/common/safe_browsing/download_protection_util.cc&l=14
michael@0 349 StringEndsWith(fileName, NS_LITERAL_STRING(".apk")) ||
michael@0 350 StringEndsWith(fileName, NS_LITERAL_STRING(".bas")) ||
michael@0 351 StringEndsWith(fileName, NS_LITERAL_STRING(".bat")) ||
michael@0 352 StringEndsWith(fileName, NS_LITERAL_STRING(".cab")) ||
michael@0 353 StringEndsWith(fileName, NS_LITERAL_STRING(".cmd")) ||
michael@0 354 StringEndsWith(fileName, NS_LITERAL_STRING(".com")) ||
michael@0 355 StringEndsWith(fileName, NS_LITERAL_STRING(".exe")) ||
michael@0 356 StringEndsWith(fileName, NS_LITERAL_STRING(".hta")) ||
michael@0 357 StringEndsWith(fileName, NS_LITERAL_STRING(".msi")) ||
michael@0 358 StringEndsWith(fileName, NS_LITERAL_STRING(".pif")) ||
michael@0 359 StringEndsWith(fileName, NS_LITERAL_STRING(".reg")) ||
michael@0 360 StringEndsWith(fileName, NS_LITERAL_STRING(".scr")) ||
michael@0 361 StringEndsWith(fileName, NS_LITERAL_STRING(".vb")) ||
michael@0 362 StringEndsWith(fileName, NS_LITERAL_STRING(".vbs")) ||
michael@0 363 StringEndsWith(fileName, NS_LITERAL_STRING(".zip"));
michael@0 364 }
michael@0 365
michael@0 366 nsresult
michael@0 367 PendingLookup::LookupNext()
michael@0 368 {
michael@0 369 // We must call LookupNext or SendRemoteQuery upon return.
michael@0 370 // Look up all of the URLs that could whitelist this download.
michael@0 371 // Blacklist first.
michael@0 372 int index = mAnylistSpecs.Length() - 1;
michael@0 373 nsCString spec;
michael@0 374 bool allowlistOnly = false;
michael@0 375 if (index >= 0) {
michael@0 376 // Check the source URI and referrer.
michael@0 377 spec = mAnylistSpecs[index];
michael@0 378 mAnylistSpecs.RemoveElementAt(index);
michael@0 379 } else {
michael@0 380 // Check the allowlists next.
michael@0 381 index = mAllowlistSpecs.Length() - 1;
michael@0 382 if (index >= 0) {
michael@0 383 allowlistOnly = true;
michael@0 384 spec = mAllowlistSpecs[index];
michael@0 385 mAllowlistSpecs.RemoveElementAt(index);
michael@0 386 }
michael@0 387 }
michael@0 388 if (index >= 0) {
michael@0 389 nsRefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
michael@0 390 return lookup->LookupSpec(spec, allowlistOnly);
michael@0 391 }
michael@0 392 #ifdef XP_WIN
michael@0 393 // There are no more URIs to check against local list. If the file is not
michael@0 394 // eligible for remote lookup, bail.
michael@0 395 if (!IsBinaryFile()) {
michael@0 396 LOG(("Not eligible for remote lookups [this=%x]", this));
michael@0 397 return OnComplete(false, NS_OK);
michael@0 398 }
michael@0 399 // Send the remote query if we are on Windows.
michael@0 400 nsresult rv = SendRemoteQuery();
michael@0 401 if (NS_FAILED(rv)) {
michael@0 402 return OnComplete(false, rv);
michael@0 403 }
michael@0 404 return NS_OK;
michael@0 405 #else
michael@0 406 return OnComplete(false, NS_OK);
michael@0 407 #endif
michael@0 408 }
michael@0 409
michael@0 410 nsCString
michael@0 411 PendingLookup::EscapeCertificateAttribute(const nsACString& aAttribute)
michael@0 412 {
michael@0 413 // Escape '/' because it's a field separator, and '%' because Chrome does
michael@0 414 nsCString escaped;
michael@0 415 escaped.SetCapacity(aAttribute.Length());
michael@0 416 for (unsigned int i = 0; i < aAttribute.Length(); ++i) {
michael@0 417 if (aAttribute.Data()[i] == '%') {
michael@0 418 escaped.Append("%25");
michael@0 419 } else if (aAttribute.Data()[i] == '/') {
michael@0 420 escaped.Append("%2F");
michael@0 421 } else if (aAttribute.Data()[i] == ' ') {
michael@0 422 escaped.Append("%20");
michael@0 423 } else {
michael@0 424 escaped.Append(aAttribute.Data()[i]);
michael@0 425 }
michael@0 426 }
michael@0 427 return escaped;
michael@0 428 }
michael@0 429
michael@0 430 nsCString
michael@0 431 PendingLookup::EscapeFingerprint(const nsACString& aFingerprint)
michael@0 432 {
michael@0 433 // Google's fingerprint doesn't have colons
michael@0 434 nsCString escaped;
michael@0 435 escaped.SetCapacity(aFingerprint.Length());
michael@0 436 for (unsigned int i = 0; i < aFingerprint.Length(); ++i) {
michael@0 437 if (aFingerprint.Data()[i] != ':') {
michael@0 438 escaped.Append(aFingerprint.Data()[i]);
michael@0 439 }
michael@0 440 }
michael@0 441 return escaped;
michael@0 442 }
michael@0 443
michael@0 444 nsresult
michael@0 445 PendingLookup::GenerateWhitelistStringsForPair(
michael@0 446 nsIX509Cert* certificate,
michael@0 447 nsIX509Cert* issuer)
michael@0 448 {
michael@0 449 // The whitelist paths have format:
michael@0 450 // http://sb-ssl.google.com/safebrowsing/csd/certificate/<issuer_cert_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
michael@0 451 // Any of CN, O, or OU may be omitted from the whitelist entry. Unfortunately
michael@0 452 // this is not publicly documented, but the Chrome implementation can be found
michael@0 453 // here:
michael@0 454 // https://code.google.com/p/chromium/codesearch#search/&q=GetCertificateWhitelistStrings
michael@0 455 nsCString whitelistString(
michael@0 456 "http://sb-ssl.google.com/safebrowsing/csd/certificate/");
michael@0 457
michael@0 458 nsString fingerprint;
michael@0 459 nsresult rv = issuer->GetSha1Fingerprint(fingerprint);
michael@0 460 NS_ENSURE_SUCCESS(rv, rv);
michael@0 461 whitelistString.Append(
michael@0 462 EscapeFingerprint(NS_ConvertUTF16toUTF8(fingerprint)));
michael@0 463
michael@0 464 nsString commonName;
michael@0 465 rv = certificate->GetCommonName(commonName);
michael@0 466 NS_ENSURE_SUCCESS(rv, rv);
michael@0 467 if (!commonName.IsEmpty()) {
michael@0 468 whitelistString.Append("/CN=");
michael@0 469 whitelistString.Append(
michael@0 470 EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(commonName)));
michael@0 471 }
michael@0 472
michael@0 473 nsString organization;
michael@0 474 rv = certificate->GetOrganization(organization);
michael@0 475 NS_ENSURE_SUCCESS(rv, rv);
michael@0 476 if (!organization.IsEmpty()) {
michael@0 477 whitelistString.Append("/O=");
michael@0 478 whitelistString.Append(
michael@0 479 EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organization)));
michael@0 480 }
michael@0 481
michael@0 482 nsString organizationalUnit;
michael@0 483 rv = certificate->GetOrganizationalUnit(organizationalUnit);
michael@0 484 NS_ENSURE_SUCCESS(rv, rv);
michael@0 485 if (!organizationalUnit.IsEmpty()) {
michael@0 486 whitelistString.Append("/OU=");
michael@0 487 whitelistString.Append(
michael@0 488 EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organizationalUnit)));
michael@0 489 }
michael@0 490 LOG(("Whitelisting %s", whitelistString.get()));
michael@0 491
michael@0 492 mAllowlistSpecs.AppendElement(whitelistString);
michael@0 493 return NS_OK;
michael@0 494 }
michael@0 495
michael@0 496 nsresult
michael@0 497 PendingLookup::GenerateWhitelistStringsForChain(
michael@0 498 const safe_browsing::ClientDownloadRequest_CertificateChain& aChain)
michael@0 499 {
michael@0 500 // We need a signing certificate and an issuer to construct a whitelist
michael@0 501 // entry.
michael@0 502 if (aChain.element_size() < 2) {
michael@0 503 return NS_OK;
michael@0 504 }
michael@0 505
michael@0 506 // Get the signer.
michael@0 507 nsresult rv;
michael@0 508 nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
michael@0 509 NS_ENSURE_SUCCESS(rv, rv);
michael@0 510
michael@0 511 nsCOMPtr<nsIX509Cert> signer;
michael@0 512 rv = certDB->ConstructX509(
michael@0 513 const_cast<char *>(aChain.element(0).certificate().data()),
michael@0 514 aChain.element(0).certificate().size(), getter_AddRefs(signer));
michael@0 515 NS_ENSURE_SUCCESS(rv, rv);
michael@0 516
michael@0 517 for (int i = 1; i < aChain.element_size(); ++i) {
michael@0 518 // Get the issuer.
michael@0 519 nsCOMPtr<nsIX509Cert> issuer;
michael@0 520 rv = certDB->ConstructX509(
michael@0 521 const_cast<char *>(aChain.element(i).certificate().data()),
michael@0 522 aChain.element(i).certificate().size(), getter_AddRefs(issuer));
michael@0 523 NS_ENSURE_SUCCESS(rv, rv);
michael@0 524
michael@0 525 nsresult rv = GenerateWhitelistStringsForPair(signer, issuer);
michael@0 526 NS_ENSURE_SUCCESS(rv, rv);
michael@0 527 }
michael@0 528 return NS_OK;
michael@0 529 }
michael@0 530
michael@0 531 nsresult
michael@0 532 PendingLookup::GenerateWhitelistStrings(
michael@0 533 const safe_browsing::ClientDownloadRequest_SignatureInfo& aSignatureInfo)
michael@0 534 {
michael@0 535 for (int i = 0; i < aSignatureInfo.certificate_chain_size(); ++i) {
michael@0 536 nsresult rv = GenerateWhitelistStringsForChain(
michael@0 537 aSignatureInfo.certificate_chain(i));
michael@0 538 NS_ENSURE_SUCCESS(rv, rv);
michael@0 539 }
michael@0 540 return NS_OK;
michael@0 541 }
michael@0 542
michael@0 543 nsresult
michael@0 544 PendingLookup::StartLookup()
michael@0 545 {
michael@0 546 mStartTime = TimeStamp::Now();
michael@0 547 nsresult rv = DoLookupInternal();
michael@0 548 if (NS_FAILED(rv)) {
michael@0 549 return OnComplete(false, NS_OK);
michael@0 550 };
michael@0 551 return rv;
michael@0 552 }
michael@0 553
michael@0 554 nsresult
michael@0 555 PendingLookup::DoLookupInternal()
michael@0 556 {
michael@0 557 // We want to check the target URI against the local lists.
michael@0 558 nsCOMPtr<nsIURI> uri;
michael@0 559 nsresult rv = mQuery->GetSourceURI(getter_AddRefs(uri));
michael@0 560 NS_ENSURE_SUCCESS(rv, rv);
michael@0 561
michael@0 562 nsCString spec;
michael@0 563 rv = uri->GetSpec(spec);
michael@0 564 NS_ENSURE_SUCCESS(rv, rv);
michael@0 565 mAnylistSpecs.AppendElement(spec);
michael@0 566
michael@0 567 nsCOMPtr<nsIURI> referrer = nullptr;
michael@0 568 rv = mQuery->GetReferrerURI(getter_AddRefs(referrer));
michael@0 569 if (referrer) {
michael@0 570 nsCString spec;
michael@0 571 rv = referrer->GetSpec(spec);
michael@0 572 NS_ENSURE_SUCCESS(rv, rv);
michael@0 573 mAnylistSpecs.AppendElement(spec);
michael@0 574 }
michael@0 575
michael@0 576 // Extract the signature and parse certificates so we can use it to check
michael@0 577 // whitelists.
michael@0 578 nsCOMPtr<nsIArray> sigArray;
michael@0 579 rv = mQuery->GetSignatureInfo(getter_AddRefs(sigArray));
michael@0 580 NS_ENSURE_SUCCESS(rv, rv);
michael@0 581
michael@0 582 if (sigArray) {
michael@0 583 rv = ParseCertificates(sigArray, &mSignatureInfo);
michael@0 584 NS_ENSURE_SUCCESS(rv, rv);
michael@0 585 }
michael@0 586
michael@0 587 rv = GenerateWhitelistStrings(mSignatureInfo);
michael@0 588 NS_ENSURE_SUCCESS(rv, rv);
michael@0 589
michael@0 590 // Start the call chain.
michael@0 591 return LookupNext();
michael@0 592 }
michael@0 593
michael@0 594 nsresult
michael@0 595 PendingLookup::OnComplete(bool shouldBlock, nsresult rv)
michael@0 596 {
michael@0 597 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
michael@0 598 shouldBlock);
michael@0 599 #if defined(PR_LOGGING)
michael@0 600 double t = (TimeStamp::Now() - mStartTime).ToMilliseconds();
michael@0 601 #endif
michael@0 602 if (shouldBlock) {
michael@0 603 LOG(("Application Reputation check failed, blocking bad binary in %f ms "
michael@0 604 "[this = %p]", t, this));
michael@0 605 } else {
michael@0 606 LOG(("Application Reputation check passed in %f ms [this = %p]", t, this));
michael@0 607 }
michael@0 608 nsresult res = mCallback->OnComplete(shouldBlock, rv);
michael@0 609 return res;
michael@0 610 }
michael@0 611
michael@0 612 nsresult
michael@0 613 PendingLookup::ParseCertificates(
michael@0 614 nsIArray* aSigArray,
michael@0 615 ClientDownloadRequest_SignatureInfo* aSignatureInfo)
michael@0 616 {
michael@0 617 // If we haven't been set for any reason, bail.
michael@0 618 NS_ENSURE_ARG_POINTER(aSigArray);
michael@0 619
michael@0 620 // Binaries may be signed by multiple chains of certificates. If there are no
michael@0 621 // chains, the binary is unsigned (or we were unable to extract signature
michael@0 622 // information on a non-Windows platform)
michael@0 623 nsCOMPtr<nsISimpleEnumerator> chains;
michael@0 624 nsresult rv = aSigArray->Enumerate(getter_AddRefs(chains));
michael@0 625 NS_ENSURE_SUCCESS(rv, rv);
michael@0 626
michael@0 627 bool hasMoreChains = false;
michael@0 628 rv = chains->HasMoreElements(&hasMoreChains);
michael@0 629 NS_ENSURE_SUCCESS(rv, rv);
michael@0 630
michael@0 631 while (hasMoreChains) {
michael@0 632 nsCOMPtr<nsISupports> supports;
michael@0 633 rv = chains->GetNext(getter_AddRefs(supports));
michael@0 634 NS_ENSURE_SUCCESS(rv, rv);
michael@0 635
michael@0 636 nsCOMPtr<nsIX509CertList> certList = do_QueryInterface(supports, &rv);
michael@0 637 NS_ENSURE_SUCCESS(rv, rv);
michael@0 638
michael@0 639 safe_browsing::ClientDownloadRequest_CertificateChain* certChain =
michael@0 640 aSignatureInfo->add_certificate_chain();
michael@0 641 nsCOMPtr<nsISimpleEnumerator> chainElt;
michael@0 642 rv = certList->GetEnumerator(getter_AddRefs(chainElt));
michael@0 643 NS_ENSURE_SUCCESS(rv, rv);
michael@0 644
michael@0 645 // Each chain may have multiple certificates.
michael@0 646 bool hasMoreCerts = false;
michael@0 647 rv = chainElt->HasMoreElements(&hasMoreCerts);
michael@0 648 while (hasMoreCerts) {
michael@0 649 nsCOMPtr<nsISupports> supports;
michael@0 650 rv = chainElt->GetNext(getter_AddRefs(supports));
michael@0 651 NS_ENSURE_SUCCESS(rv, rv);
michael@0 652
michael@0 653 nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(supports, &rv);
michael@0 654 NS_ENSURE_SUCCESS(rv, rv);
michael@0 655
michael@0 656 uint8_t* data = nullptr;
michael@0 657 uint32_t len = 0;
michael@0 658 rv = cert->GetRawDER(&len, &data);
michael@0 659 NS_ENSURE_SUCCESS(rv, rv);
michael@0 660
michael@0 661 // Add this certificate to the protobuf to send remotely.
michael@0 662 certChain->add_element()->set_certificate(data, len);
michael@0 663 nsMemory::Free(data);
michael@0 664
michael@0 665 rv = chainElt->HasMoreElements(&hasMoreCerts);
michael@0 666 NS_ENSURE_SUCCESS(rv, rv);
michael@0 667 }
michael@0 668 rv = chains->HasMoreElements(&hasMoreChains);
michael@0 669 NS_ENSURE_SUCCESS(rv, rv);
michael@0 670 }
michael@0 671 if (aSignatureInfo->certificate_chain_size() > 0) {
michael@0 672 aSignatureInfo->set_trusted(true);
michael@0 673 }
michael@0 674 return NS_OK;
michael@0 675 }
michael@0 676
michael@0 677 nsresult
michael@0 678 PendingLookup::SendRemoteQuery()
michael@0 679 {
michael@0 680 nsresult rv = SendRemoteQueryInternal();
michael@0 681 if (NS_FAILED(rv)) {
michael@0 682 return OnComplete(false, NS_OK);
michael@0 683 }
michael@0 684 // SendRemoteQueryInternal has fired off the query and we call OnComplete in
michael@0 685 // the nsIStreamListener.onStopRequest.
michael@0 686 return rv;
michael@0 687 }
michael@0 688
michael@0 689 nsresult
michael@0 690 PendingLookup::SendRemoteQueryInternal()
michael@0 691 {
michael@0 692 LOG(("Sending remote query for application reputation [this = %p]", this));
michael@0 693 // We did not find a local result, so fire off the query to the application
michael@0 694 // reputation service.
michael@0 695 safe_browsing::ClientDownloadRequest req;
michael@0 696 nsCOMPtr<nsIURI> uri;
michael@0 697 nsresult rv;
michael@0 698 rv = mQuery->GetSourceURI(getter_AddRefs(uri));
michael@0 699 NS_ENSURE_SUCCESS(rv, rv);
michael@0 700 nsCString spec;
michael@0 701 rv = uri->GetSpec(spec);
michael@0 702 NS_ENSURE_SUCCESS(rv, rv);
michael@0 703 req.set_url(spec.get());
michael@0 704
michael@0 705 uint32_t fileSize;
michael@0 706 rv = mQuery->GetFileSize(&fileSize);
michael@0 707 NS_ENSURE_SUCCESS(rv, rv);
michael@0 708 req.set_length(fileSize);
michael@0 709 // We have no way of knowing whether or not a user initiated the download.
michael@0 710 req.set_user_initiated(false);
michael@0 711
michael@0 712 nsCString locale;
michael@0 713 NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_GENERAL_LOCALE, &locale),
michael@0 714 NS_ERROR_NOT_AVAILABLE);
michael@0 715 req.set_locale(locale.get());
michael@0 716 nsCString sha256Hash;
michael@0 717 rv = mQuery->GetSha256Hash(sha256Hash);
michael@0 718 NS_ENSURE_SUCCESS(rv, rv);
michael@0 719 req.mutable_digests()->set_sha256(sha256Hash.Data());
michael@0 720 nsString fileName;
michael@0 721 rv = mQuery->GetSuggestedFileName(fileName);
michael@0 722 NS_ENSURE_SUCCESS(rv, rv);
michael@0 723 req.set_file_basename(NS_ConvertUTF16toUTF8(fileName).get());
michael@0 724 req.mutable_signature()->CopyFrom(mSignatureInfo);
michael@0 725
michael@0 726 if (req.signature().trusted()) {
michael@0 727 LOG(("Got signed binary for remote application reputation check "
michael@0 728 "[this = %p]", this));
michael@0 729 } else {
michael@0 730 LOG(("Got unsigned binary for remote application reputation check "
michael@0 731 "[this = %p]", this));
michael@0 732 }
michael@0 733
michael@0 734 // Serialize the protocol buffer to a string. This can only fail if we are
michael@0 735 // out of memory, or if the protocol buffer req is missing required fields
michael@0 736 // (only the URL for now).
michael@0 737 std::string serialized;
michael@0 738 if (!req.SerializeToString(&serialized)) {
michael@0 739 return NS_ERROR_UNEXPECTED;
michael@0 740 }
michael@0 741
michael@0 742 // Set the input stream to the serialized protocol buffer
michael@0 743 nsCOMPtr<nsIStringInputStream> sstream =
michael@0 744 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
michael@0 745 NS_ENSURE_SUCCESS(rv, rv);
michael@0 746
michael@0 747 rv = sstream->SetData(serialized.c_str(), serialized.length());
michael@0 748 NS_ENSURE_SUCCESS(rv, rv);
michael@0 749
michael@0 750 // Set up the channel to transmit the request to the service.
michael@0 751 nsCOMPtr<nsIChannel> channel;
michael@0 752 nsCString serviceUrl;
michael@0 753 NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
michael@0 754 NS_ERROR_NOT_AVAILABLE);
michael@0 755 nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
michael@0 756 rv = ios->NewChannel(serviceUrl, nullptr, nullptr, getter_AddRefs(channel));
michael@0 757 NS_ENSURE_SUCCESS(rv, rv);
michael@0 758
michael@0 759 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel, &rv));
michael@0 760 NS_ENSURE_SUCCESS(rv, rv);
michael@0 761
michael@0 762 // Upload the protobuf to the application reputation service.
michael@0 763 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel, &rv);
michael@0 764 NS_ENSURE_SUCCESS(rv, rv);
michael@0 765
michael@0 766 rv = uploadChannel->ExplicitSetUploadStream(sstream,
michael@0 767 NS_LITERAL_CSTRING("application/octet-stream"), serialized.size(),
michael@0 768 NS_LITERAL_CSTRING("POST"), false);
michael@0 769 NS_ENSURE_SUCCESS(rv, rv);
michael@0 770
michael@0 771 // Set the Safebrowsing cookie jar, so that the regular Google cookie is not
michael@0 772 // sent with this request. See bug 897516.
michael@0 773 nsCOMPtr<nsIInterfaceRequestor> loadContext =
michael@0 774 new mozilla::LoadContext(NECKO_SAFEBROWSING_APP_ID);
michael@0 775 rv = channel->SetNotificationCallbacks(loadContext);
michael@0 776 NS_ENSURE_SUCCESS(rv, rv);
michael@0 777
michael@0 778 rv = channel->AsyncOpen(this, nullptr);
michael@0 779 NS_ENSURE_SUCCESS(rv, rv);
michael@0 780
michael@0 781 return NS_OK;
michael@0 782 }
michael@0 783
michael@0 784 ////////////////////////////////////////////////////////////////////////////////
michael@0 785 //// nsIStreamListener
michael@0 786 static NS_METHOD
michael@0 787 AppendSegmentToString(nsIInputStream* inputStream,
michael@0 788 void *closure,
michael@0 789 const char *rawSegment,
michael@0 790 uint32_t toOffset,
michael@0 791 uint32_t count,
michael@0 792 uint32_t *writeCount) {
michael@0 793 nsAutoCString* decodedData = static_cast<nsAutoCString*>(closure);
michael@0 794 decodedData->Append(rawSegment, count);
michael@0 795 *writeCount = count;
michael@0 796 return NS_OK;
michael@0 797 }
michael@0 798
michael@0 799 NS_IMETHODIMP
michael@0 800 PendingLookup::OnDataAvailable(nsIRequest *aRequest,
michael@0 801 nsISupports *aContext,
michael@0 802 nsIInputStream *aStream,
michael@0 803 uint64_t offset,
michael@0 804 uint32_t count) {
michael@0 805 uint32_t read;
michael@0 806 return aStream->ReadSegments(AppendSegmentToString, &mResponse, count, &read);
michael@0 807 }
michael@0 808
michael@0 809 NS_IMETHODIMP
michael@0 810 PendingLookup::OnStartRequest(nsIRequest *aRequest,
michael@0 811 nsISupports *aContext) {
michael@0 812 return NS_OK;
michael@0 813 }
michael@0 814
michael@0 815 NS_IMETHODIMP
michael@0 816 PendingLookup::OnStopRequest(nsIRequest *aRequest,
michael@0 817 nsISupports *aContext,
michael@0 818 nsresult aResult) {
michael@0 819 NS_ENSURE_STATE(mCallback);
michael@0 820
michael@0 821 bool shouldBlock = false;
michael@0 822 nsresult rv = OnStopRequestInternal(aRequest, aContext, aResult,
michael@0 823 &shouldBlock);
michael@0 824 OnComplete(shouldBlock, rv);
michael@0 825 return rv;
michael@0 826 }
michael@0 827
michael@0 828 nsresult
michael@0 829 PendingLookup::OnStopRequestInternal(nsIRequest *aRequest,
michael@0 830 nsISupports *aContext,
michael@0 831 nsresult aResult,
michael@0 832 bool* aShouldBlock) {
michael@0 833 if (NS_FAILED(aResult)) {
michael@0 834 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
michael@0 835 SERVER_RESPONSE_FAILED);
michael@0 836 return aResult;
michael@0 837 }
michael@0 838
michael@0 839 *aShouldBlock = false;
michael@0 840 nsresult rv;
michael@0 841 nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
michael@0 842 if (NS_FAILED(rv)) {
michael@0 843 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
michael@0 844 SERVER_RESPONSE_FAILED);
michael@0 845 return rv;
michael@0 846 }
michael@0 847
michael@0 848 uint32_t status = 0;
michael@0 849 rv = channel->GetResponseStatus(&status);
michael@0 850 if (NS_FAILED(rv)) {
michael@0 851 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
michael@0 852 SERVER_RESPONSE_FAILED);
michael@0 853 return rv;
michael@0 854 }
michael@0 855
michael@0 856 if (status != 200) {
michael@0 857 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
michael@0 858 SERVER_RESPONSE_FAILED);
michael@0 859 return NS_ERROR_NOT_AVAILABLE;
michael@0 860 }
michael@0 861
michael@0 862 std::string buf(mResponse.Data(), mResponse.Length());
michael@0 863 safe_browsing::ClientDownloadResponse response;
michael@0 864 if (!response.ParseFromString(buf)) {
michael@0 865 NS_WARNING("Could not parse protocol buffer");
michael@0 866 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
michael@0 867 SERVER_RESPONSE_INVALID);
michael@0 868 return NS_ERROR_CANNOT_CONVERT_DATA;
michael@0 869 }
michael@0 870
michael@0 871 // There are several more verdicts, but we only respect one for now and treat
michael@0 872 // everything else as SAFE.
michael@0 873 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
michael@0 874 SERVER_RESPONSE_VALID);
michael@0 875 if (response.verdict() == safe_browsing::ClientDownloadResponse::DANGEROUS) {
michael@0 876 *aShouldBlock = true;
michael@0 877 }
michael@0 878
michael@0 879 return NS_OK;
michael@0 880 }
michael@0 881
michael@0 882 NS_IMPL_ISUPPORTS(ApplicationReputationService,
michael@0 883 nsIApplicationReputationService)
michael@0 884
michael@0 885 ApplicationReputationService*
michael@0 886 ApplicationReputationService::gApplicationReputationService = nullptr;
michael@0 887
michael@0 888 ApplicationReputationService*
michael@0 889 ApplicationReputationService::GetSingleton()
michael@0 890 {
michael@0 891 if (gApplicationReputationService) {
michael@0 892 NS_ADDREF(gApplicationReputationService);
michael@0 893 return gApplicationReputationService;
michael@0 894 }
michael@0 895
michael@0 896 // We're not initialized yet.
michael@0 897 gApplicationReputationService = new ApplicationReputationService();
michael@0 898 if (gApplicationReputationService) {
michael@0 899 NS_ADDREF(gApplicationReputationService);
michael@0 900 }
michael@0 901
michael@0 902 return gApplicationReputationService;
michael@0 903 }
michael@0 904
michael@0 905 ApplicationReputationService::ApplicationReputationService()
michael@0 906 {
michael@0 907 #if defined(PR_LOGGING)
michael@0 908 if (!prlog) {
michael@0 909 prlog = PR_NewLogModule("ApplicationReputation");
michael@0 910 }
michael@0 911 #endif
michael@0 912 LOG(("Application reputation service started up"));
michael@0 913 }
michael@0 914
michael@0 915 ApplicationReputationService::~ApplicationReputationService() {
michael@0 916 LOG(("Application reputation service shutting down"));
michael@0 917 }
michael@0 918
michael@0 919 NS_IMETHODIMP
michael@0 920 ApplicationReputationService::QueryReputation(
michael@0 921 nsIApplicationReputationQuery* aQuery,
michael@0 922 nsIApplicationReputationCallback* aCallback) {
michael@0 923 NS_ENSURE_ARG_POINTER(aQuery);
michael@0 924 NS_ENSURE_ARG_POINTER(aCallback);
michael@0 925
michael@0 926 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true);
michael@0 927 nsresult rv = QueryReputationInternal(aQuery, aCallback);
michael@0 928 if (NS_FAILED(rv)) {
michael@0 929 Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
michael@0 930 false);
michael@0 931 aCallback->OnComplete(false, rv);
michael@0 932 }
michael@0 933 return NS_OK;
michael@0 934 }
michael@0 935
michael@0 936 nsresult ApplicationReputationService::QueryReputationInternal(
michael@0 937 nsIApplicationReputationQuery* aQuery,
michael@0 938 nsIApplicationReputationCallback* aCallback) {
michael@0 939 nsresult rv;
michael@0 940 // If malware checks aren't enabled, don't query application reputation.
michael@0 941 if (!Preferences::GetBool(PREF_SB_MALWARE_ENABLED, false)) {
michael@0 942 return NS_ERROR_NOT_AVAILABLE;
michael@0 943 }
michael@0 944
michael@0 945 // If there is no service URL for querying application reputation, abort.
michael@0 946 nsCString serviceUrl;
michael@0 947 NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
michael@0 948 NS_ERROR_NOT_AVAILABLE);
michael@0 949 if (serviceUrl.EqualsLiteral("")) {
michael@0 950 return NS_ERROR_NOT_AVAILABLE;
michael@0 951 }
michael@0 952
michael@0 953 nsCOMPtr<nsIURI> uri;
michael@0 954 rv = aQuery->GetSourceURI(getter_AddRefs(uri));
michael@0 955 NS_ENSURE_SUCCESS(rv, rv);
michael@0 956 // Bail if the URI hasn't been set.
michael@0 957 NS_ENSURE_STATE(uri);
michael@0 958
michael@0 959 // Create a new pending lookup and start the call chain.
michael@0 960 nsRefPtr<PendingLookup> lookup(new PendingLookup(aQuery, aCallback));
michael@0 961 NS_ENSURE_STATE(lookup);
michael@0 962
michael@0 963 return lookup->StartLookup();
michael@0 964 }

mercurial