Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
aktualizr_secondary.cc
1 #include "aktualizr_secondary.h"
2 
3 #include "crypto/keymanager.h"
4 #include "logging/logging.h"
5 #include "update_agent.h"
6 #include "uptane/manifest.h"
7 #include "utilities/utils.h"
8 
9 #include <sys/types.h>
10 #include <memory>
11 
12 AktualizrSecondary::AktualizrSecondary(AktualizrSecondaryConfig config, std::shared_ptr<INvStorage> storage,
13  std::shared_ptr<KeyManager> key_mngr, std::shared_ptr<UpdateAgent> update_agent)
14  : config_(std::move(config)),
15  storage_(std::move(storage)),
16  keys_(std::move(key_mngr)),
17  update_agent_(std::move(update_agent)) {
18  uptaneInitialize();
19  manifest_issuer_ = std::make_shared<Uptane::ManifestIssuer>(keys_, ecu_serial_);
20  initPendingTargetIfAny();
21 
22  if (hasPendingUpdate()) {
23  // TODO(OTA-4545): refactor this to make it simpler as we don't need to persist/store
24  // an installation status of each ECU but store it just for a given secondary ECU
25  std::vector<Uptane::Target> installed_versions;
26  boost::optional<Uptane::Target> pending_target;
27  storage_->loadInstalledVersions(ecu_serial_.ToString(), nullptr, &pending_target);
28 
29  if (!!pending_target) {
30  data::InstallationResult install_res =
31  data::InstallationResult(data::ResultCode::Numeric::kUnknown, "Unknown installation error");
32  LOG_INFO << "Pending update found; attempting to apply it. Target hash: " << pending_target->sha256Hash();
33 
34  install_res = update_agent_->applyPendingInstall(*pending_target);
35 
36  if (install_res.result_code != data::ResultCode::Numeric::kNeedCompletion) {
37  storage_->saveEcuInstallationResult(ecu_serial_, install_res);
38 
39  if (install_res.success) {
40  LOG_INFO << "Pending update has been successfully applied: " << pending_target->sha256Hash();
41  storage_->saveInstalledVersion(ecu_serial_.ToString(), *pending_target, InstalledVersionUpdateMode::kCurrent);
42  } else {
43  LOG_ERROR << "Application of the pending update has failed: (" << install_res.result_code.toString() << ")"
44  << install_res.description;
45  storage_->saveInstalledVersion(ecu_serial_.ToString(), *pending_target, InstalledVersionUpdateMode::kNone);
46  }
47 
48  director_repo_.dropTargets(*storage_);
49  } else {
50  LOG_INFO << "Pending update hasn't been applied because a reboot hasn't been detected";
51  }
52  }
53  }
54 }
55 
56 Uptane::EcuSerial AktualizrSecondary::getSerial() const { return ecu_serial_; }
57 
58 Uptane::HardwareIdentifier AktualizrSecondary::getHwId() const { return hardware_id_; }
59 
60 PublicKey AktualizrSecondary::getPublicKey() const { return keys_->UptanePublicKey(); }
61 
62 Uptane::Manifest AktualizrSecondary::getManifest() const {
63  Uptane::InstalledImageInfo installed_image_info;
64  Uptane::Manifest manifest;
65  if (update_agent_->getInstalledImageInfo(installed_image_info)) {
66  manifest = manifest_issuer_->assembleAndSignManifest(installed_image_info);
67  }
68 
69  return manifest;
70 }
71 
72 int32_t AktualizrSecondary::getRootVersion(bool director) const {
73  std::string root_meta;
74  if (!storage_->loadLatestRoot(&root_meta,
75  (director) ? Uptane::RepositoryType::Director() : Uptane::RepositoryType::Image())) {
76  LOG_ERROR << "Could not load root metadata";
77  return -1;
78  }
79 
80  return Uptane::extractVersionUntrusted(root_meta);
81 }
82 
83 bool AktualizrSecondary::putRoot(const std::string& root, bool director) {
84  (void)root;
85  (void)director;
86  LOG_ERROR << "putRootResp is not implemented yet";
87  return false;
88 }
89 
90 bool AktualizrSecondary::putMetadata(const Metadata& metadata) { return doFullVerification(metadata); }
91 
92 bool AktualizrSecondary::sendFirmware(const std::string& firmware) {
93  if (!pending_target_.IsValid()) {
94  LOG_ERROR << "Aborting image download/receiving; no valid target found.";
95  return false;
96  }
97 
98  if (!update_agent_->download(pending_target_, firmware)) {
99  LOG_ERROR << "Failed to pull/store an update data";
100  pending_target_ = Uptane::Target::Unknown();
101  return false;
102  }
103 
104  return true;
105 }
106 
107 data::ResultCode::Numeric AktualizrSecondary::install(const std::string& target_name) {
108  if (!pending_target_.IsValid()) {
109  LOG_ERROR << "Aborting target image installation; no valid target found.";
111  }
112 
113  if (pending_target_.filename() != target_name) {
114  LOG_ERROR << "name of the target to install and a name of the pending target do not match";
116  }
117 
118  auto install_result = update_agent_->install(pending_target_);
119 
120  switch (install_result) {
121  case data::ResultCode::Numeric::kOk: {
122  storage_->saveInstalledVersion(ecu_serial_.ToString(), pending_target_, InstalledVersionUpdateMode::kCurrent);
123  pending_target_ = Uptane::Target::Unknown();
124  LOG_INFO << "The target has been successfully installed: " << target_name;
125  break;
126  }
127  case data::ResultCode::Numeric::kNeedCompletion: {
128  storage_->saveInstalledVersion(ecu_serial_.ToString(), pending_target_, InstalledVersionUpdateMode::kPending);
129  LOG_INFO << "The target has been successfully installed, but a reboot is required to be applied: " << target_name;
130  break;
131  }
132  default: { LOG_INFO << "Failed to install the target: " << target_name; }
133  }
134 
135  return install_result;
136 }
137 
138 bool AktualizrSecondary::doFullVerification(const Metadata& metadata) {
139  // 5.4.4.2. Full verification https://uptane.github.io/uptane-standard/uptane-standard.html#metadata_verification
140 
141  // 1. Load and verify the current time or the most recent securely attested time.
142  //
143  // We trust the time that the given system/OS/ECU provides, In ECU We Trust :)
144  TimeStamp now(TimeStamp::Now());
145 
146  // 2. Download and check the Root metadata file from the Director repository, following the procedure in
147  // Section 5.4.4.3. DirectorRepository::updateMeta() method implements this verification step, certain steps are
148  // missing though. see the method source code for details
149 
150  // 3. NOT SUPPORTED: Download and check the Timestamp metadata file from the Director repository, following the
151  // procedure in Section 5.4.4.4.
152  // 4. NOT SUPPORTED: Download and check the Snapshot metadata file from the Director repository, following the
153  // procedure in Section 5.4.4.5.
154  //
155  // 5. Download and check the Targets metadata file from the Director repository, following the procedure in
156  // Section 5.4.4.6. DirectorRepository::updateMeta() method implements this verification step
157  //
158  // The followin steps of the Director's target metadata verification are missing in DirectorRepository::updateMeta()
159  // 6. If checking Targets metadata from the Director repository, verify that there are no delegations.
160  // 7. If checking Targets metadata from the Director repository, check that no ECU identifier is represented more
161  // than once.
162  if (!director_repo_.updateMeta(*storage_, metadata)) {
163  LOG_ERROR << "Failed to update director metadata: " << director_repo_.getLastException().what();
164  return false;
165  }
166 
167  // 6. Download and check the Root metadata file from the Image repository, following the procedure in Section 5.4.4.3.
168  // 7. Download and check the Timestamp metadata file from the Image repository, following the procedure in
169  // Section 5.4.4.4.
170  // 8. Download and check the Snapshot metadata file from the Image repository, following the procedure in
171  // Section 5.4.4.5.
172  // 9. Download and check the top-level Targets metadata file from the Image repository, following the procedure in
173  // Section 5.4.4.6.
174  if (!image_repo_.updateMeta(*storage_, metadata)) {
175  LOG_ERROR << "Failed to update image metadata: " << image_repo_.getLastException().what();
176  return false;
177  }
178 
179  // 10. Verify that Targets metadata from the Director and Image repositories match.
180  if (!director_repo_.matchTargetsWithImageTargets(*(image_repo_.getTargets()))) {
181  LOG_ERROR << "Targets metadata from the Director and Image repositories DOES NOT match ";
182  return false;
183  }
184 
185  auto targetsForThisEcu = director_repo_.getTargets(getSerial(), getHwId());
186 
187  if (targetsForThisEcu.size() != 1) {
188  LOG_ERROR << "Invalid number of targets (should be 1): " << targetsForThisEcu.size();
189  return false;
190  }
191 
192  if (!update_agent_->isTargetSupported(targetsForThisEcu[0])) {
193  LOG_ERROR << "The given target type is not supported: " << targetsForThisEcu[0].type();
194  return false;
195  }
196 
197  pending_target_ = targetsForThisEcu[0];
198 
199  return true;
200 }
201 
202 void AktualizrSecondary::uptaneInitialize() {
203  if (keys_->generateUptaneKeyPair().size() == 0) {
204  throw std::runtime_error("Failed to generate uptane key pair");
205  }
206 
207  // from uptane/initialize.cc but we only take care of our own serial/hwid
208  EcuSerials ecu_serials;
209 
210  if (storage_->loadEcuSerials(&ecu_serials)) {
211  ecu_serial_ = ecu_serials[0].first;
212  hardware_id_ = ecu_serials[0].second;
213  return;
214  }
215 
216  std::string ecu_serial_local = config_.uptane.ecu_serial;
217  if (ecu_serial_local.empty()) {
218  ecu_serial_local = keys_->UptanePublicKey().KeyId();
219  }
220 
221  std::string ecu_hardware_id = config_.uptane.ecu_hardware_id;
222  if (ecu_hardware_id.empty()) {
223  ecu_hardware_id = Utils::getHostname();
224  if (ecu_hardware_id == "") {
225  throw std::runtime_error("Failed to define ECU hardware ID");
226  }
227  }
228 
229  ecu_serials.emplace_back(Uptane::EcuSerial(ecu_serial_local), Uptane::HardwareIdentifier(ecu_hardware_id));
230  storage_->storeEcuSerials(ecu_serials);
231  ecu_serial_ = ecu_serials[0].first;
232  hardware_id_ = ecu_serials[0].second;
233 
234  // this is a way to find out and store a value of the target name that is installed
235  // at the initial/provisioning stage and included into a device manifest
236  // i.e. 'filepath' field or ["signed"]["installed_image"]["filepath"]
237  // this value must match the value pushed to the backend during the bitbaking process,
238  // specifically, at its ostree push phase and is equal to
239  // GARAGE_TARGET_NAME ?= "${OSTREE_BRANCHNAME}" which in turn is equal to OSTREE_BRANCHNAME ?= "${SOTA_HARDWARE_ID}"
240  // therefore, by default GARAGE_TARGET_NAME == OSTREE_BRANCHNAME == SOTA_HARDWARE_ID
241  // If there is no match then the backend/UI will not render/highlight currently installed version at all/correctly
242  storage_->importInstalledVersions(config_.import.base_path);
243 }
244 
245 void AktualizrSecondary::initPendingTargetIfAny() {
246  if (!director_repo_.checkMetaOffline(*storage_)) {
247  LOG_INFO << "No any valid and pending director's targets to be applied";
248  return;
249  }
250 
251  auto targetsForThisEcu = director_repo_.getTargets(ecu_serial_, hardware_id_);
252 
253  if (targetsForThisEcu.size() != 1) {
254  LOG_ERROR << "Invalid number of targets (should be 1): " << targetsForThisEcu.size();
255  return;
256  }
257 
258  if (!update_agent_->isTargetSupported(targetsForThisEcu[0])) {
259  LOG_ERROR << "The given target type is not supported: " << targetsForThisEcu[0].type();
260  return;
261  }
262 
263  LOG_INFO << "There is a valid and pending director's target to be applied";
264  pending_target_ = targetsForThisEcu[0];
265 }
data::InstallationResult
Definition: types.h:182
AktualizrSecondaryConfig
Definition: aktualizr_secondary_config.h:39
Uptane::HardwareIdentifier
Definition: tuf.h:143
TimeStamp
Definition: types.h:86
Uptane::InstalledImageInfo
Definition: tuf.h:132
Uptane::EcuSerial
Definition: tuf.h:174
PublicKey
Definition: crypto.h:26
data::ResultCode::Numeric::kInternalError
SWM Internal integrity error.
data::ResultCode::Numeric
Numeric
Definition: types.h:128
Metadata
Definition: aktualizr_secondary_metadata.h:8
Uptane::Manifest
Definition: manifest.h:13