AU: Detect and report public key verify failures, but continue updating.

Make a new OmahaEvent (Type: 14 (kTypeUpdateDownloadFinished), Result:
0 (kResultError), ErrorCode: 18
(kActionCodeDownloadPayloadPubKeyVerificationError)). This event is
sent if we have a successful download that fails public key
verification. Currently this is a non-fatal failure, but we plan to
make it fatal in future clients.

BUG=chromium-os:13341
TEST=unittests; 3 on device tests: w/o public key on device, w/ pub
key and matching download, w/ pub key and non-matching download

Change-Id: Ib6589c78449c2dfcbffa4c85ab679f7fe844584b

Review URL: http://codereview.chromium.org/6792065
diff --git a/action_processor.h b/action_processor.h
index ef06181..7f7862a 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -41,6 +41,7 @@
   kActionCodeNewRootfsVerificationError = 15,
   kActionCodeNewKernelVerificationError = 16,
   kActionCodeSignedDeltaPayloadExpectedError = 17,
+  kActionCodeDownloadPayloadPubKeyVerificationError = 18,
   kActionCodeOmahaRequestEmptyResponseError = 200,
   kActionCodeOmahaRequestXMLParseError = 201,
   kActionCodeOmahaRequestNoUpdateCheckNode = 202,
diff --git a/download_action.cc b/download_action.cc
index 9577047..933cbdd 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -27,7 +27,8 @@
       http_fetcher_(http_fetcher),
       code_(kActionCodeSuccess),
       delegate_(NULL),
-      bytes_received_(0) {}
+      bytes_received_(0),
+      skip_reporting_signature_fail_(NULL) {}
 
 DownloadAction::~DownloadAction() {}
 
@@ -157,6 +158,7 @@
   if (delegate_) {
     delegate_->SetDownloadStatus(false);  // Set to inactive.
   }
+  bool signature_verify_failed = false;
   ActionExitCode code =
       successful ? kActionCodeSuccess : kActionCodeDownloadTransferError;
   if (code == kActionCodeSuccess) {
@@ -164,7 +166,7 @@
       if (!delta_performer_->VerifyPayload("",
                                            install_plan_.download_hash,
                                            install_plan_.size,
-                                           NULL)) {
+                                           &signature_verify_failed)) {
         LOG(ERROR) << "Download of " << install_plan_.download_url
                    << " failed due to payload verification error.";
         code = kActionCodeDownloadPayloadVerificationError;
@@ -193,6 +195,12 @@
     }
   }
 
+  if (skip_reporting_signature_fail_.get() &&
+      (code != kActionCodeSuccess || !signature_verify_failed)) {
+    LOG(INFO) << "Suppressing signature pub key verification warning";
+    skip_reporting_signature_fail_->Run();
+  }
+
   FlushLinuxCaches();
 
   // Write the path to the output pipe if we're successful.
diff --git a/download_action.h b/download_action.h
index 6106740..3a7dfe8 100644
--- a/download_action.h
+++ b/download_action.h
@@ -13,6 +13,7 @@
 
 #include <base/scoped_ptr.h>
 #include <curl/curl.h>
+#include <google/protobuf/stubs/common.h>
 
 #include "update_engine/action.h"
 #include "update_engine/decompressing_file_writer.h"
@@ -97,6 +98,10 @@
 
   HttpFetcher* http_fetcher() { return http_fetcher_.get(); }
 
+  void set_skip_reporting_signature_fail(google::protobuf::Closure* callback) {
+    skip_reporting_signature_fail_.reset(callback);
+  }
+
  private:
   // The InstallPlan passed in
   InstallPlan install_plan_;
@@ -133,6 +138,9 @@
   DownloadActionDelegate* delegate_;
   uint64_t bytes_received_;
 
+  // Called if the download fails OR (download success AND signature verifies)
+  scoped_ptr<google::protobuf::Closure> skip_reporting_signature_fail_;
+
   DISALLOW_COPY_AND_ASSIGN(DownloadAction);
 };
 
diff --git a/update_attempter.cc b/update_attempter.cc
index c9ee712..f56c0d1 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -32,6 +32,7 @@
 
 using base::TimeDelta;
 using base::TimeTicks;
+using google::protobuf::NewPermanentCallback;
 using std::make_pair;
 using std::tr1::shared_ptr;
 using std::string;
@@ -188,6 +189,21 @@
   shared_ptr<DownloadAction> download_action(
       new DownloadAction(prefs_, new MultiRangeHTTPFetcher(
           new LibcurlHttpFetcher(GetProxyResolver()))));
+  // This action is always initially in place to warn OS vendor of a
+  // signature failure.  If it's not needed, it will be told to skip.
+  shared_ptr<OmahaRequestAction> download_signature_warning(
+      new OmahaRequestAction(
+          prefs_,
+          omaha_request_params_,
+          new OmahaEvent(
+              OmahaEvent::kTypeUpdateDownloadFinished,
+              OmahaEvent::kResultError,
+              kActionCodeDownloadPayloadPubKeyVerificationError),
+          new LibcurlHttpFetcher(GetProxyResolver())));
+  download_action->set_skip_reporting_signature_fail(
+      NewPermanentCallback(download_signature_warning.get(),
+                           &OmahaRequestAction::set_should_skip,
+                           true));
   shared_ptr<OmahaRequestAction> download_finished_action(
       new OmahaRequestAction(prefs_,
                              omaha_request_params_,
@@ -217,6 +233,7 @@
       kernel_filesystem_copier_action));
   actions_.push_back(shared_ptr<AbstractAction>(download_started_action));
   actions_.push_back(shared_ptr<AbstractAction>(download_action));
+  actions_.push_back(shared_ptr<AbstractAction>(download_signature_warning));
   actions_.push_back(shared_ptr<AbstractAction>(download_finished_action));
   actions_.push_back(shared_ptr<AbstractAction>(filesystem_verifier_action));
   actions_.push_back(shared_ptr<AbstractAction>(
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 73addb5..2c5c33c 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -252,6 +252,7 @@
   OmahaRequestAction::StaticType(),
   DownloadAction::StaticType(),
   OmahaRequestAction::StaticType(),
+  OmahaRequestAction::StaticType(),
   FilesystemCopierAction::StaticType(),
   FilesystemCopierAction::StaticType(),
   PostinstallRunnerAction::StaticType(),