Home | History | Annotate | Download | only in extensions
      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 #include "chrome/browser/extensions/install_verifier.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 
     10 #include "base/bind.h"
     11 #include "base/command_line.h"
     12 #include "base/metrics/field_trial.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/prefs/pref_service.h"
     15 #include "base/stl_util.h"
     16 #include "chrome/browser/extensions/extension_management.h"
     17 #include "chrome/browser/extensions/extension_service.h"
     18 #include "chrome/browser/extensions/install_signer.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "chrome/common/extensions/manifest_url_handler.h"
     21 #include "chrome/common/pref_names.h"
     22 #include "chrome/grit/generated_resources.h"
     23 #include "content/public/browser/browser_context.h"
     24 #include "content/public/common/content_switches.h"
     25 #include "extensions/browser/extension_prefs.h"
     26 #include "extensions/browser/extension_registry.h"
     27 #include "extensions/browser/extension_system.h"
     28 #include "extensions/browser/pref_names.h"
     29 #include "extensions/common/extension_set.h"
     30 #include "extensions/common/manifest.h"
     31 #include "extensions/common/one_shot_event.h"
     32 #include "ui/base/l10n/l10n_util.h"
     33 
     34 namespace extensions {
     35 
     36 namespace {
     37 
     38 enum VerifyStatus {
     39   NONE = 0,   // Do not request install signatures, and do not enforce them.
     40   BOOTSTRAP,  // Request install signatures, but do not enforce them.
     41   ENFORCE,    // Request install signatures, and enforce them.
     42   ENFORCE_STRICT,  // Same as ENFORCE, but hard fail if we can't fetch
     43                    // signatures.
     44 
     45   // This is used in histograms - do not remove or reorder entries above! Also
     46   // the "MAX" item below should always be the last element.
     47   VERIFY_STATUS_MAX
     48 };
     49 
     50 #if defined(GOOGLE_CHROME_BUILD)
     51 const char kExperimentName[] = "ExtensionInstallVerification";
     52 #endif  // defined(GOOGLE_CHROME_BUILD)
     53 
     54 VerifyStatus GetExperimentStatus() {
     55 #if defined(GOOGLE_CHROME_BUILD)
     56   const std::string group = base::FieldTrialList::FindFullName(
     57       kExperimentName);
     58 
     59   std::string forced_trials = CommandLine::ForCurrentProcess()->
     60       GetSwitchValueASCII(switches::kForceFieldTrials);
     61   if (forced_trials.find(kExperimentName) != std::string::npos) {
     62     // We don't want to allow turning off enforcement by forcing the field
     63     // trial group to something other than enforcement.
     64     return ENFORCE_STRICT;
     65   }
     66 
     67   VerifyStatus default_status = NONE;
     68 
     69   if (group == "EnforceStrict")
     70     return ENFORCE_STRICT;
     71   else if (group == "Enforce")
     72     return ENFORCE;
     73   else if (group == "Bootstrap")
     74     return BOOTSTRAP;
     75   else if (group == "None" || group == "Control")
     76     return NONE;
     77   else
     78     return default_status;
     79 #endif  // defined(GOOGLE_CHROME_BUILD)
     80 
     81   return NONE;
     82 }
     83 
     84 VerifyStatus GetCommandLineStatus() {
     85   const CommandLine* cmdline = CommandLine::ForCurrentProcess();
     86   if (!InstallSigner::GetForcedNotFromWebstore().empty())
     87     return ENFORCE;
     88 
     89   if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) {
     90     std::string value = cmdline->GetSwitchValueASCII(
     91         switches::kExtensionsInstallVerification);
     92     if (value == "bootstrap")
     93       return BOOTSTRAP;
     94     else if (value == "enforce_strict")
     95       return ENFORCE_STRICT;
     96     else
     97       return ENFORCE;
     98   }
     99 
    100   return NONE;
    101 }
    102 
    103 VerifyStatus GetStatus() {
    104   return std::max(GetExperimentStatus(), GetCommandLineStatus());
    105 }
    106 
    107 bool ShouldFetchSignature() {
    108   return GetStatus() >= BOOTSTRAP;
    109 }
    110 
    111 bool ShouldEnforce() {
    112   return GetStatus() >= ENFORCE;
    113 }
    114 
    115 enum InitResult {
    116   INIT_NO_PREF = 0,
    117   INIT_UNPARSEABLE_PREF,
    118   INIT_INVALID_SIGNATURE,
    119   INIT_VALID_SIGNATURE,
    120 
    121   // This is used in histograms - do not remove or reorder entries above! Also
    122   // the "MAX" item below should always be the last element.
    123 
    124   INIT_RESULT_MAX
    125 };
    126 
    127 void LogInitResultHistogram(InitResult result) {
    128   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
    129                             result, INIT_RESULT_MAX);
    130 }
    131 
    132 bool CanUseExtensionApis(const Extension& extension) {
    133   return extension.is_extension() || extension.is_legacy_packaged_app();
    134 }
    135 
    136 enum VerifyAllSuccess {
    137   VERIFY_ALL_BOOTSTRAP_SUCCESS = 0,
    138   VERIFY_ALL_BOOTSTRAP_FAILURE,
    139   VERIFY_ALL_NON_BOOTSTRAP_SUCCESS,
    140   VERIFY_ALL_NON_BOOTSTRAP_FAILURE,
    141 
    142   // Used in histograms. Do not remove/reorder any entries above, and the below
    143   // MAX entry should always come last.
    144   VERIFY_ALL_SUCCESS_MAX
    145 };
    146 
    147 // Record the success or failure of verifying all extensions, and whether or
    148 // not it was a bootstrapping.
    149 void LogVerifyAllSuccessHistogram(bool bootstrap, bool success) {
    150   VerifyAllSuccess result;
    151   if (bootstrap && success)
    152     result = VERIFY_ALL_BOOTSTRAP_SUCCESS;
    153   else if (bootstrap && !success)
    154     result = VERIFY_ALL_BOOTSTRAP_FAILURE;
    155   else if (!bootstrap && success)
    156     result = VERIFY_ALL_NON_BOOTSTRAP_SUCCESS;
    157   else
    158     result = VERIFY_ALL_NON_BOOTSTRAP_FAILURE;
    159 
    160   // This used to be part of ExtensionService, but moved here. In order to keep
    161   // our histograms accurate, the name is unchanged.
    162   UMA_HISTOGRAM_ENUMERATION(
    163       "ExtensionService.VerifyAllSuccess", result, VERIFY_ALL_SUCCESS_MAX);
    164 }
    165 
    166 // Record the success or failure of a single verification.
    167 void LogAddVerifiedSuccess(bool success) {
    168   // This used to be part of ExtensionService, but moved here. In order to keep
    169   // our histograms accurate, the name is unchanged.
    170   UMA_HISTOGRAM_BOOLEAN("ExtensionService.AddVerified", success);
    171 }
    172 
    173 }  // namespace
    174 
    175 InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
    176                                  content::BrowserContext* context)
    177     : prefs_(prefs),
    178       context_(context),
    179       bootstrap_check_complete_(false),
    180       weak_factory_(this) {
    181 }
    182 
    183 InstallVerifier::~InstallVerifier() {}
    184 
    185 // static
    186 bool InstallVerifier::NeedsVerification(const Extension& extension) {
    187   return IsFromStore(extension) && CanUseExtensionApis(extension);
    188 }
    189 
    190 
    191 
    192 // static
    193 bool InstallVerifier::IsFromStore(const Extension& extension) {
    194   if (extension.from_webstore() || ManifestURL::UpdatesFromGallery(&extension))
    195     return true;
    196 
    197   // If an extension has no update url, our autoupdate code will ask the
    198   // webstore about it (to aid in migrating to the webstore from self-hosting
    199   // or sideloading based installs). So we want to do verification checks on
    200   // such extensions too so that we don't accidentally disable old installs of
    201   // extensions that did migrate to the webstore.
    202   return (ManifestURL::GetUpdateURL(&extension).is_empty() &&
    203           Manifest::IsAutoUpdateableLocation(extension.location()));
    204 }
    205 
    206 void InstallVerifier::Init() {
    207   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
    208                             GetExperimentStatus(), VERIFY_STATUS_MAX);
    209   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
    210                             GetStatus(), VERIFY_STATUS_MAX);
    211 
    212   const base::DictionaryValue* pref = prefs_->GetInstallSignature();
    213   if (pref) {
    214     scoped_ptr<InstallSignature> signature_from_prefs =
    215         InstallSignature::FromValue(*pref);
    216     if (!signature_from_prefs.get()) {
    217       LogInitResultHistogram(INIT_UNPARSEABLE_PREF);
    218     } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) {
    219       LogInitResultHistogram(INIT_INVALID_SIGNATURE);
    220       DVLOG(1) << "Init - ignoring invalid signature";
    221     } else {
    222       signature_ = signature_from_prefs.Pass();
    223       LogInitResultHistogram(INIT_VALID_SIGNATURE);
    224       UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
    225                                signature_->ids.size());
    226       GarbageCollect();
    227     }
    228   } else {
    229     LogInitResultHistogram(INIT_NO_PREF);
    230   }
    231 
    232   ExtensionSystem::Get(context_)->ready().Post(
    233       FROM_HERE,
    234       base::Bind(&InstallVerifier::MaybeBootstrapSelf,
    235                  weak_factory_.GetWeakPtr()));
    236 }
    237 
    238 void InstallVerifier::VerifyAllExtensions() {
    239   AddMany(GetExtensionsToVerify(), ADD_ALL);
    240 }
    241 
    242 base::Time InstallVerifier::SignatureTimestamp() {
    243   if (signature_.get())
    244     return signature_->timestamp;
    245   else
    246     return base::Time();
    247 }
    248 
    249 bool InstallVerifier::IsKnownId(const std::string& id) const {
    250   return signature_.get() && (ContainsKey(signature_->ids, id) ||
    251                               ContainsKey(signature_->invalid_ids, id));
    252 }
    253 
    254 bool InstallVerifier::IsInvalid(const std::string& id) const {
    255   return ((signature_.get() && ContainsKey(signature_->invalid_ids, id)));
    256 }
    257 
    258 void InstallVerifier::VerifyExtension(const std::string& extension_id) {
    259   ExtensionIdSet ids;
    260   ids.insert(extension_id);
    261   AddMany(ids, ADD_SINGLE);
    262 }
    263 
    264 void InstallVerifier::AddMany(const ExtensionIdSet& ids, OperationType type) {
    265   if (!ShouldFetchSignature()) {
    266     OnVerificationComplete(true, type);  // considered successful.
    267     return;
    268   }
    269 
    270   if (signature_.get()) {
    271     ExtensionIdSet not_allowed_yet =
    272         base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids);
    273     if (not_allowed_yet.empty()) {
    274       OnVerificationComplete(true, type);  // considered successful.
    275       return;
    276     }
    277   }
    278 
    279   InstallVerifier::PendingOperation* operation =
    280       new InstallVerifier::PendingOperation(type);
    281   operation->ids.insert(ids.begin(), ids.end());
    282 
    283   operation_queue_.push(linked_ptr<PendingOperation>(operation));
    284 
    285   // If there are no ongoing pending requests, we need to kick one off.
    286   if (operation_queue_.size() == 1)
    287     BeginFetch();
    288 }
    289 
    290 void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) {
    291   provisional_.insert(ids.begin(), ids.end());
    292   AddMany(ids, ADD_PROVISIONAL);
    293 }
    294 
    295 void InstallVerifier::Remove(const std::string& id) {
    296   ExtensionIdSet ids;
    297   ids.insert(id);
    298   RemoveMany(ids);
    299 }
    300 
    301 void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
    302   if (!signature_.get() || !ShouldFetchSignature())
    303     return;
    304 
    305   bool found_any = false;
    306   for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
    307     if (ContainsKey(signature_->ids, *i) ||
    308         ContainsKey(signature_->invalid_ids, *i)) {
    309       found_any = true;
    310       break;
    311     }
    312   }
    313   if (!found_any)
    314     return;
    315 
    316   InstallVerifier::PendingOperation* operation =
    317       new InstallVerifier::PendingOperation(InstallVerifier::REMOVE);
    318   operation->ids = ids;
    319 
    320   operation_queue_.push(linked_ptr<PendingOperation>(operation));
    321   if (operation_queue_.size() == 1)
    322     BeginFetch();
    323 }
    324 
    325 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const {
    326   return ExtensionManagementFactory::GetForBrowserContext(context_)
    327       ->IsInstallationExplicitlyAllowed(id);
    328 }
    329 
    330 std::string InstallVerifier::GetDebugPolicyProviderName() const {
    331   return std::string("InstallVerifier");
    332 }
    333 
    334 namespace {
    335 
    336 enum MustRemainDisabledOutcome {
    337   VERIFIED = 0,
    338   NOT_EXTENSION,
    339   UNPACKED,
    340   ENTERPRISE_POLICY_ALLOWED,
    341   FORCED_NOT_VERIFIED,
    342   NOT_FROM_STORE,
    343   NO_SIGNATURE,
    344   NOT_VERIFIED_BUT_NOT_ENFORCING,
    345   NOT_VERIFIED,
    346   NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE,
    347   NOT_VERIFIED_BUT_UNKNOWN_ID,
    348   COMPONENT,
    349 
    350   // This is used in histograms - do not remove or reorder entries above! Also
    351   // the "MAX" item below should always be the last element.
    352   MUST_REMAIN_DISABLED_OUTCOME_MAX
    353 };
    354 
    355 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome) {
    356   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
    357                             outcome, MUST_REMAIN_DISABLED_OUTCOME_MAX);
    358 }
    359 
    360 }  // namespace
    361 
    362 bool InstallVerifier::MustRemainDisabled(const Extension* extension,
    363                                          Extension::DisableReason* reason,
    364                                          base::string16* error) const {
    365   CHECK(extension);
    366   if (!CanUseExtensionApis(*extension)) {
    367     MustRemainDisabledHistogram(NOT_EXTENSION);
    368     return false;
    369   }
    370   if (Manifest::IsUnpackedLocation(extension->location())) {
    371     MustRemainDisabledHistogram(UNPACKED);
    372     return false;
    373   }
    374   if (extension->location() == Manifest::COMPONENT) {
    375     MustRemainDisabledHistogram(COMPONENT);
    376     return false;
    377   }
    378   if (AllowedByEnterprisePolicy(extension->id())) {
    379     MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED);
    380     return false;
    381   }
    382 
    383   bool verified = true;
    384   MustRemainDisabledOutcome outcome = VERIFIED;
    385   if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id())) {
    386     verified = false;
    387     outcome = FORCED_NOT_VERIFIED;
    388   } else if (!IsFromStore(*extension)) {
    389     verified = false;
    390     outcome = NOT_FROM_STORE;
    391   } else if (signature_.get() == NULL &&
    392              (!bootstrap_check_complete_ || GetStatus() < ENFORCE_STRICT)) {
    393     // If we don't have a signature yet, we'll temporarily consider every
    394     // extension from the webstore verified to avoid false positives on existing
    395     // profiles hitting this code for the first time. The InstallVerifier
    396     // will bootstrap itself once the ExtensionsSystem is ready.
    397     outcome = NO_SIGNATURE;
    398   } else if (!IsVerified(extension->id())) {
    399     if (signature_.get() &&
    400         !ContainsKey(signature_->invalid_ids, extension->id())) {
    401       outcome = NOT_VERIFIED_BUT_UNKNOWN_ID;
    402     } else {
    403       verified = false;
    404       outcome = NOT_VERIFIED;
    405     }
    406   }
    407   if (!verified && !ShouldEnforce()) {
    408     verified = true;
    409     outcome = NOT_VERIFIED_BUT_NOT_ENFORCING;
    410   }
    411   MustRemainDisabledHistogram(outcome);
    412 
    413   if (!verified) {
    414     if (reason)
    415       *reason = Extension::DISABLE_NOT_VERIFIED;
    416     if (error)
    417       *error = l10n_util::GetStringFUTF16(
    418           IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
    419           l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
    420   }
    421   return !verified;
    422 }
    423 
    424 InstallVerifier::PendingOperation::PendingOperation(OperationType type)
    425     : type(type) {}
    426 
    427 InstallVerifier::PendingOperation::~PendingOperation() {
    428 }
    429 
    430 ExtensionIdSet InstallVerifier::GetExtensionsToVerify() const {
    431   ExtensionIdSet result;
    432   scoped_ptr<ExtensionSet> extensions =
    433       ExtensionRegistry::Get(context_)->GenerateInstalledExtensionsSet();
    434   for (ExtensionSet::const_iterator iter = extensions->begin();
    435        iter != extensions->end();
    436        ++iter) {
    437     if (NeedsVerification(*iter->get()))
    438       result.insert((*iter)->id());
    439   }
    440   return result;
    441 }
    442 
    443 void InstallVerifier::MaybeBootstrapSelf() {
    444   bool needs_bootstrap = false;
    445 
    446   ExtensionIdSet extension_ids = GetExtensionsToVerify();
    447   if (signature_.get() == NULL && ShouldFetchSignature()) {
    448     needs_bootstrap = true;
    449   } else {
    450     for (ExtensionIdSet::const_iterator iter = extension_ids.begin();
    451          iter != extension_ids.end();
    452          ++iter) {
    453       if (!IsKnownId(*iter)) {
    454         needs_bootstrap = true;
    455         break;
    456       }
    457     }
    458   }
    459 
    460   if (needs_bootstrap)
    461     AddMany(extension_ids, ADD_ALL_BOOTSTRAP);
    462   else
    463     bootstrap_check_complete_ = true;
    464 }
    465 
    466 void InstallVerifier::OnVerificationComplete(bool success, OperationType type) {
    467   switch (type) {
    468     case ADD_SINGLE:
    469       LogAddVerifiedSuccess(success);
    470       break;
    471     case ADD_ALL:
    472     case ADD_ALL_BOOTSTRAP:
    473       LogVerifyAllSuccessHistogram(type == ADD_ALL_BOOTSTRAP, success);
    474       bootstrap_check_complete_ = true;
    475       if (success) {
    476         // Iterate through the extensions and, if any are newly-verified and
    477         // should have the DISABLE_NOT_VERIFIED reason lifted, do so.
    478         const ExtensionSet& disabled_extensions =
    479             ExtensionRegistry::Get(context_)->disabled_extensions();
    480         for (ExtensionSet::const_iterator iter = disabled_extensions.begin();
    481              iter != disabled_extensions.end();
    482              ++iter) {
    483           int disable_reasons = prefs_->GetDisableReasons((*iter)->id());
    484           if (disable_reasons & Extension::DISABLE_NOT_VERIFIED &&
    485               !MustRemainDisabled(iter->get(), NULL, NULL)) {
    486             prefs_->RemoveDisableReason((*iter)->id(),
    487                                         Extension::DISABLE_NOT_VERIFIED);
    488           }
    489         }
    490       }
    491       if (success || GetStatus() == ENFORCE_STRICT) {
    492         ExtensionSystem::Get(context_)
    493             ->extension_service()
    494             ->CheckManagementPolicy();
    495       }
    496       break;
    497     // We don't need to check disable reasons or report UMA stats for
    498     // provisional adds or removals.
    499     case ADD_PROVISIONAL:
    500     case REMOVE:
    501       break;
    502   }
    503 }
    504 
    505 void InstallVerifier::GarbageCollect() {
    506   if (!ShouldFetchSignature()) {
    507     return;
    508   }
    509   CHECK(signature_.get());
    510   ExtensionIdSet leftovers = signature_->ids;
    511   leftovers.insert(signature_->invalid_ids.begin(),
    512                    signature_->invalid_ids.end());
    513   ExtensionIdList all_ids;
    514   prefs_->GetExtensions(&all_ids);
    515   for (ExtensionIdList::const_iterator i = all_ids.begin();
    516        i != all_ids.end(); ++i) {
    517     ExtensionIdSet::iterator found = leftovers.find(*i);
    518     if (found != leftovers.end())
    519       leftovers.erase(found);
    520   }
    521   if (!leftovers.empty()) {
    522     RemoveMany(leftovers);
    523   }
    524 }
    525 
    526 bool InstallVerifier::IsVerified(const std::string& id) const {
    527   return ((signature_.get() && ContainsKey(signature_->ids, id)) ||
    528           ContainsKey(provisional_, id));
    529 }
    530 
    531 void InstallVerifier::BeginFetch() {
    532   DCHECK(ShouldFetchSignature());
    533 
    534   // TODO(asargent) - It would be possible to coalesce all operations in the
    535   // queue into one fetch - we'd probably just need to change the queue to
    536   // hold (set of ids, list of operation type) pairs.
    537   CHECK(!operation_queue_.empty());
    538   const PendingOperation& operation = *operation_queue_.front();
    539 
    540   ExtensionIdSet ids_to_sign;
    541   if (signature_.get()) {
    542     ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
    543   }
    544   if (operation.type == InstallVerifier::REMOVE) {
    545     for (ExtensionIdSet::const_iterator i = operation.ids.begin();
    546          i != operation.ids.end(); ++i) {
    547       if (ContainsKey(ids_to_sign, *i))
    548         ids_to_sign.erase(*i);
    549     }
    550   } else {  // All other operation types are some form of "ADD".
    551     ids_to_sign.insert(operation.ids.begin(), operation.ids.end());
    552   }
    553 
    554   signer_.reset(new InstallSigner(context_->GetRequestContext(), ids_to_sign));
    555   signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback,
    556                                    weak_factory_.GetWeakPtr()));
    557 }
    558 
    559 void InstallVerifier::SaveToPrefs() {
    560   if (signature_.get())
    561     DCHECK(InstallSigner::VerifySignature(*signature_));
    562 
    563   if (!signature_.get() || signature_->ids.empty()) {
    564     DVLOG(1) << "SaveToPrefs - saving NULL";
    565     prefs_->SetInstallSignature(NULL);
    566   } else {
    567     base::DictionaryValue pref;
    568     signature_->ToValue(&pref);
    569     if (VLOG_IS_ON(1)) {
    570       DVLOG(1) << "SaveToPrefs - saving";
    571 
    572       DCHECK(InstallSigner::VerifySignature(*signature_.get()));
    573       scoped_ptr<InstallSignature> rehydrated =
    574           InstallSignature::FromValue(pref);
    575       DCHECK(InstallSigner::VerifySignature(*rehydrated.get()));
    576     }
    577     prefs_->SetInstallSignature(&pref);
    578   }
    579 }
    580 
    581 namespace {
    582 
    583 enum CallbackResult {
    584   CALLBACK_NO_SIGNATURE = 0,
    585   CALLBACK_INVALID_SIGNATURE,
    586   CALLBACK_VALID_SIGNATURE,
    587 
    588   // This is used in histograms - do not remove or reorder entries above! Also
    589   // the "MAX" item below should always be the last element.
    590 
    591   CALLBACK_RESULT_MAX
    592 };
    593 
    594 void GetSignatureResultHistogram(CallbackResult result) {
    595   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
    596                             result, CALLBACK_RESULT_MAX);
    597 }
    598 
    599 }  // namespace
    600 
    601 void InstallVerifier::SignatureCallback(
    602     scoped_ptr<InstallSignature> signature) {
    603 
    604   linked_ptr<PendingOperation> operation = operation_queue_.front();
    605   operation_queue_.pop();
    606 
    607   bool success = false;
    608   if (!signature.get()) {
    609     GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE);
    610   } else if (!InstallSigner::VerifySignature(*signature)) {
    611     GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE);
    612   } else {
    613     GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE);
    614     success = true;
    615   }
    616 
    617   if (!success) {
    618     OnVerificationComplete(false, operation->type);
    619 
    620     // TODO(asargent) - if this was something like a network error, we need to
    621     // do retries with exponential back off.
    622   } else {
    623     signature_ = signature.Pass();
    624     SaveToPrefs();
    625 
    626     if (!provisional_.empty()) {
    627       // Update |provisional_| to remove ids that were successfully signed.
    628       provisional_ = base::STLSetDifference<ExtensionIdSet>(
    629           provisional_, signature_->ids);
    630     }
    631 
    632     OnVerificationComplete(success, operation->type);
    633   }
    634 
    635   if (!operation_queue_.empty())
    636     BeginFetch();
    637 }
    638 
    639 }  // namespace extensions
    640