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