1//
2// Copyright (C) 2011 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/omaha_response_handler_action.h"
18
19#include <string>
20
21#include <base/files/file_util.h>
22#include <base/files/scoped_temp_dir.h>
23#include <gtest/gtest.h>
24
25#include "update_engine/common/constants.h"
26#include "update_engine/common/platform_constants.h"
27#include "update_engine/common/test_utils.h"
28#include "update_engine/common/utils.h"
29#include "update_engine/fake_system_state.h"
30#include "update_engine/mock_payload_state.h"
31#include "update_engine/payload_consumer/payload_constants.h"
32
33using chromeos_update_engine::test_utils::System;
34using chromeos_update_engine::test_utils::WriteFileString;
35using std::string;
36using testing::Return;
37using testing::_;
38
39namespace chromeos_update_engine {
40
41class OmahaResponseHandlerActionTest : public ::testing::Test {
42 protected:
43  void SetUp() override {
44    FakeBootControl* fake_boot_control = fake_system_state_.fake_boot_control();
45    fake_boot_control->SetPartitionDevice(
46        kLegacyPartitionNameKernel, 0, "/dev/sdz2");
47    fake_boot_control->SetPartitionDevice(
48        kLegacyPartitionNameRoot, 0, "/dev/sdz3");
49    fake_boot_control->SetPartitionDevice(
50        kLegacyPartitionNameKernel, 1, "/dev/sdz4");
51    fake_boot_control->SetPartitionDevice(
52        kLegacyPartitionNameRoot, 1, "/dev/sdz5");
53  }
54
55  // Return true iff the OmahaResponseHandlerAction succeeded.
56  // If out is non-null, it's set w/ the response from the action.
57  bool DoTest(const OmahaResponse& in,
58              const string& deadline_file,
59              InstallPlan* out);
60
61  FakeSystemState fake_system_state_;
62};
63
64class OmahaResponseHandlerActionProcessorDelegate
65    : public ActionProcessorDelegate {
66 public:
67  OmahaResponseHandlerActionProcessorDelegate()
68      : code_(ErrorCode::kError),
69        code_set_(false) {}
70  void ActionCompleted(ActionProcessor* processor,
71                       AbstractAction* action,
72                       ErrorCode code) {
73    if (action->Type() == OmahaResponseHandlerAction::StaticType()) {
74      code_ = code;
75      code_set_ = true;
76    }
77  }
78  ErrorCode code_;
79  bool code_set_;
80};
81
82namespace {
83const char* const kLongName =
84    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
85    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
86    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
87    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
88    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
89    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
90    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
91    "-the_update_a.b.c.d_DELTA_.tgz";
92const char* const kBadVersion = "don't update me";
93}  // namespace
94
95bool OmahaResponseHandlerActionTest::DoTest(
96    const OmahaResponse& in,
97    const string& test_deadline_file,
98    InstallPlan* out) {
99  ActionProcessor processor;
100  OmahaResponseHandlerActionProcessorDelegate delegate;
101  processor.set_delegate(&delegate);
102
103  ObjectFeederAction<OmahaResponse> feeder_action;
104  feeder_action.set_obj(in);
105  if (in.update_exists && in.version != kBadVersion) {
106    EXPECT_CALL(*(fake_system_state_.mock_prefs()),
107                SetString(kPrefsUpdateCheckResponseHash, in.hash))
108        .WillOnce(Return(true));
109
110    int slot = 1 - fake_system_state_.fake_boot_control()->GetCurrentSlot();
111    string key = kPrefsChannelOnSlotPrefix + std::to_string(slot);
112    EXPECT_CALL(*(fake_system_state_.mock_prefs()), SetString(key, testing::_))
113        .WillOnce(Return(true));
114  }
115
116  string current_url = in.payload_urls.size() ? in.payload_urls[0] : "";
117  EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetCurrentUrl())
118      .WillRepeatedly(Return(current_url));
119
120  OmahaResponseHandlerAction response_handler_action(
121      &fake_system_state_,
122      (test_deadline_file.empty() ?
123       constants::kOmahaResponseDeadlineFile : test_deadline_file));
124  BondActions(&feeder_action, &response_handler_action);
125  ObjectCollectorAction<InstallPlan> collector_action;
126  BondActions(&response_handler_action, &collector_action);
127  processor.EnqueueAction(&feeder_action);
128  processor.EnqueueAction(&response_handler_action);
129  processor.EnqueueAction(&collector_action);
130  processor.StartProcessing();
131  EXPECT_TRUE(!processor.IsRunning())
132      << "Update test to handle non-async actions";
133  if (out)
134    *out = collector_action.object();
135  EXPECT_TRUE(delegate.code_set_);
136  return delegate.code_ == ErrorCode::kSuccess;
137}
138
139TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
140  string test_deadline_file;
141  CHECK(utils::MakeTempFile(
142          "omaha_response_handler_action_unittest-XXXXXX",
143          &test_deadline_file, nullptr));
144  ScopedPathUnlinker deadline_unlinker(test_deadline_file);
145  {
146    OmahaResponse in;
147    in.update_exists = true;
148    in.version = "a.b.c.d";
149    in.payload_urls.push_back("http://foo/the_update_a.b.c.d.tgz");
150    in.more_info_url = "http://more/info";
151    in.hash = "HASH+";
152    in.size = 12;
153    in.prompt = false;
154    in.deadline = "20101020";
155    InstallPlan install_plan;
156    EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
157    EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
158    EXPECT_EQ(in.hash, install_plan.payload_hash);
159    EXPECT_EQ(1U, install_plan.target_slot);
160    string deadline;
161    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
162    EXPECT_EQ("20101020", deadline);
163    struct stat deadline_stat;
164    EXPECT_EQ(0, stat(test_deadline_file.c_str(), &deadline_stat));
165    EXPECT_EQ(
166        static_cast<mode_t>(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH),
167        deadline_stat.st_mode);
168    EXPECT_EQ(in.version, install_plan.version);
169  }
170  {
171    OmahaResponse in;
172    in.update_exists = true;
173    in.version = "a.b.c.d";
174    in.payload_urls.push_back("http://foo/the_update_a.b.c.d.tgz");
175    in.more_info_url = "http://more/info";
176    in.hash = "HASHj+";
177    in.size = 12;
178    in.prompt = true;
179    InstallPlan install_plan;
180    // Set the other slot as current.
181    fake_system_state_.fake_boot_control()->SetCurrentSlot(1);
182    EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
183    EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
184    EXPECT_EQ(in.hash, install_plan.payload_hash);
185    EXPECT_EQ(0U, install_plan.target_slot);
186    string deadline;
187    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline) &&
188                deadline.empty());
189    EXPECT_EQ(in.version, install_plan.version);
190  }
191  {
192    OmahaResponse in;
193    in.update_exists = true;
194    in.version = "a.b.c.d";
195    in.payload_urls.push_back(kLongName);
196    in.more_info_url = "http://more/info";
197    in.hash = "HASHj+";
198    in.size = 12;
199    in.prompt = true;
200    in.deadline = "some-deadline";
201    InstallPlan install_plan;
202    fake_system_state_.fake_boot_control()->SetCurrentSlot(0);
203    EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
204    EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
205    EXPECT_EQ(in.hash, install_plan.payload_hash);
206    EXPECT_EQ(1U, install_plan.target_slot);
207    string deadline;
208    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
209    EXPECT_EQ("some-deadline", deadline);
210    EXPECT_EQ(in.version, install_plan.version);
211  }
212}
213
214TEST_F(OmahaResponseHandlerActionTest, NoUpdatesTest) {
215  OmahaResponse in;
216  in.update_exists = false;
217  InstallPlan install_plan;
218  EXPECT_FALSE(DoTest(in, "", &install_plan));
219  EXPECT_TRUE(install_plan.partitions.empty());
220}
221
222TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpTest) {
223  OmahaResponse in;
224  in.update_exists = true;
225  in.version = "a.b.c.d";
226  in.payload_urls.push_back("http://test.should/need/hash.checks.signed");
227  in.more_info_url = "http://more/info";
228  in.hash = "HASHj+";
229  in.size = 12;
230  // Hash checks are always skipped for non-official update URLs.
231  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
232              IsUpdateUrlOfficial())
233      .WillRepeatedly(Return(true));
234  InstallPlan install_plan;
235  EXPECT_TRUE(DoTest(in, "", &install_plan));
236  EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
237  EXPECT_EQ(in.hash, install_plan.payload_hash);
238  EXPECT_TRUE(install_plan.hash_checks_mandatory);
239  EXPECT_EQ(in.version, install_plan.version);
240}
241
242TEST_F(OmahaResponseHandlerActionTest, HashChecksForUnofficialUpdateUrl) {
243  OmahaResponse in;
244  in.update_exists = true;
245  in.version = "a.b.c.d";
246  in.payload_urls.push_back("http://url.normally/needs/hash.checks.signed");
247  in.more_info_url = "http://more/info";
248  in.hash = "HASHj+";
249  in.size = 12;
250  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
251              IsUpdateUrlOfficial())
252      .WillRepeatedly(Return(false));
253  InstallPlan install_plan;
254  EXPECT_TRUE(DoTest(in, "", &install_plan));
255  EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
256  EXPECT_EQ(in.hash, install_plan.payload_hash);
257  EXPECT_FALSE(install_plan.hash_checks_mandatory);
258  EXPECT_EQ(in.version, install_plan.version);
259}
260
261TEST_F(OmahaResponseHandlerActionTest,
262       HashChecksForOfficialUrlUnofficialBuildTest) {
263  // Official URLs for unofficial builds (dev/test images) don't require hash.
264  OmahaResponse in;
265  in.update_exists = true;
266  in.version = "a.b.c.d";
267  in.payload_urls.push_back("http://url.normally/needs/hash.checks.signed");
268  in.more_info_url = "http://more/info";
269  in.hash = "HASHj+";
270  in.size = 12;
271  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
272              IsUpdateUrlOfficial())
273      .WillRepeatedly(Return(true));
274  fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
275  InstallPlan install_plan;
276  EXPECT_TRUE(DoTest(in, "", &install_plan));
277  EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
278  EXPECT_EQ(in.hash, install_plan.payload_hash);
279  EXPECT_FALSE(install_plan.hash_checks_mandatory);
280  EXPECT_EQ(in.version, install_plan.version);
281}
282
283TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpsTest) {
284  OmahaResponse in;
285  in.update_exists = true;
286  in.version = "a.b.c.d";
287  in.payload_urls.push_back("https://test.should.not/need/hash.checks.signed");
288  in.more_info_url = "http://more/info";
289  in.hash = "HASHj+";
290  in.size = 12;
291  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
292              IsUpdateUrlOfficial())
293      .WillRepeatedly(Return(true));
294  InstallPlan install_plan;
295  EXPECT_TRUE(DoTest(in, "", &install_plan));
296  EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
297  EXPECT_EQ(in.hash, install_plan.payload_hash);
298  EXPECT_FALSE(install_plan.hash_checks_mandatory);
299  EXPECT_EQ(in.version, install_plan.version);
300}
301
302TEST_F(OmahaResponseHandlerActionTest, HashChecksForBothHttpAndHttpsTest) {
303  OmahaResponse in;
304  in.update_exists = true;
305  in.version = "a.b.c.d";
306  in.payload_urls.push_back("http://test.should.still/need/hash.checks");
307  in.payload_urls.push_back("https://test.should.still/need/hash.checks");
308  in.more_info_url = "http://more/info";
309  in.hash = "HASHj+";
310  in.size = 12;
311  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
312              IsUpdateUrlOfficial())
313      .WillRepeatedly(Return(true));
314  InstallPlan install_plan;
315  EXPECT_TRUE(DoTest(in, "", &install_plan));
316  EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
317  EXPECT_EQ(in.hash, install_plan.payload_hash);
318  EXPECT_TRUE(install_plan.hash_checks_mandatory);
319  EXPECT_EQ(in.version, install_plan.version);
320}
321
322TEST_F(OmahaResponseHandlerActionTest, ChangeToMoreStableChannelTest) {
323  OmahaResponse in;
324  in.update_exists = true;
325  in.version = "a.b.c.d";
326  in.payload_urls.push_back("https://MoreStableChannelTest");
327  in.more_info_url = "http://more/info";
328  in.hash = "HASHjk";
329  in.size = 15;
330
331  // Create a uniquely named test directory.
332  base::ScopedTempDir tempdir;
333  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
334
335  OmahaRequestParams params(&fake_system_state_);
336  fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
337  params.set_root(tempdir.path().value());
338  params.set_current_channel("canary-channel");
339  // The ImageProperties in Android uses prefs to store MutableImageProperties.
340#ifdef __ANDROID__
341  EXPECT_CALL(*fake_system_state_.mock_prefs(), SetString(_, "stable-channel"))
342      .WillOnce(Return(true));
343  EXPECT_CALL(*fake_system_state_.mock_prefs(), SetBoolean(_, true))
344      .WillOnce(Return(true));
345#endif  // __ANDROID__
346  EXPECT_TRUE(params.SetTargetChannel("stable-channel", true, nullptr));
347  params.UpdateDownloadChannel();
348  EXPECT_TRUE(params.to_more_stable_channel());
349  EXPECT_TRUE(params.is_powerwash_allowed());
350
351  fake_system_state_.set_request_params(&params);
352  InstallPlan install_plan;
353  EXPECT_TRUE(DoTest(in, "", &install_plan));
354  EXPECT_TRUE(install_plan.powerwash_required);
355}
356
357TEST_F(OmahaResponseHandlerActionTest, ChangeToLessStableChannelTest) {
358  OmahaResponse in;
359  in.update_exists = true;
360  in.version = "a.b.c.d";
361  in.payload_urls.push_back("https://LessStableChannelTest");
362  in.more_info_url = "http://more/info";
363  in.hash = "HASHjk";
364  in.size = 15;
365
366  // Create a uniquely named test directory.
367  base::ScopedTempDir tempdir;
368  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
369
370  OmahaRequestParams params(&fake_system_state_);
371  fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
372  params.set_root(tempdir.path().value());
373  params.set_current_channel("stable-channel");
374  // The ImageProperties in Android uses prefs to store MutableImageProperties.
375#ifdef __ANDROID__
376  EXPECT_CALL(*fake_system_state_.mock_prefs(), SetString(_, "canary-channel"))
377      .WillOnce(Return(true));
378  EXPECT_CALL(*fake_system_state_.mock_prefs(), SetBoolean(_, false))
379      .WillOnce(Return(true));
380#endif  // __ANDROID__
381  EXPECT_TRUE(params.SetTargetChannel("canary-channel", false, nullptr));
382  params.UpdateDownloadChannel();
383  EXPECT_FALSE(params.to_more_stable_channel());
384  EXPECT_FALSE(params.is_powerwash_allowed());
385
386  fake_system_state_.set_request_params(&params);
387  InstallPlan install_plan;
388  EXPECT_TRUE(DoTest(in, "", &install_plan));
389  EXPECT_FALSE(install_plan.powerwash_required);
390}
391
392TEST_F(OmahaResponseHandlerActionTest, P2PUrlIsUsedAndHashChecksMandatory) {
393  OmahaResponse in;
394  in.update_exists = true;
395  in.version = "a.b.c.d";
396  in.payload_urls.push_back("https://would.not/cause/hash/checks");
397  in.more_info_url = "http://more/info";
398  in.hash = "HASHj+";
399  in.size = 12;
400
401  OmahaRequestParams params(&fake_system_state_);
402  // We're using a real OmahaRequestParams object here so we can't mock
403  // IsUpdateUrlOfficial(), but setting the update URL to the AutoUpdate test
404  // server will cause IsUpdateUrlOfficial() to return true.
405  params.set_update_url(constants::kOmahaDefaultAUTestURL);
406  fake_system_state_.set_request_params(&params);
407
408  EXPECT_CALL(*fake_system_state_.mock_payload_state(),
409              SetUsingP2PForDownloading(true));
410
411  string p2p_url = "http://9.8.7.6/p2p";
412  EXPECT_CALL(*fake_system_state_.mock_payload_state(), GetP2PUrl())
413      .WillRepeatedly(Return(p2p_url));
414  EXPECT_CALL(*fake_system_state_.mock_payload_state(),
415              GetUsingP2PForDownloading()).WillRepeatedly(Return(true));
416
417  InstallPlan install_plan;
418  EXPECT_TRUE(DoTest(in, "", &install_plan));
419  EXPECT_EQ(in.hash, install_plan.payload_hash);
420  EXPECT_EQ(install_plan.download_url, p2p_url);
421  EXPECT_TRUE(install_plan.hash_checks_mandatory);
422}
423
424}  // namespace chromeos_update_engine
425