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(const AktualizrSecondaryConfig& config, std::shared_ptr<INvStorage> storage)
13  : config_(config),
14  storage_(std::move(storage)),
15  keys_(std::make_shared<KeyManager>(storage_, config_.keymanagerConfig())) {
16  uptaneInitialize();
17  manifest_issuer_ = std::make_shared<Uptane::ManifestIssuer>(keys_, ecu_serial_);
18  registerHandlers();
19 }
20 
21 PublicKey AktualizrSecondary::publicKey() const { return keys_->UptanePublicKey(); }
22 
23 Uptane::Manifest AktualizrSecondary::getManifest() const {
24  Uptane::InstalledImageInfo installed_image_info;
25  Uptane::Manifest manifest;
26 
27  if (getInstalledImageInfo(installed_image_info)) {
28  manifest = manifest_issuer_->assembleAndSignManifest(installed_image_info);
29  }
30 
31  return manifest;
32 }
33 
34 data::InstallationResult AktualizrSecondary::putMetadata(const Metadata& metadata) {
35  return doFullVerification(metadata);
36 }
37 
38 data::InstallationResult AktualizrSecondary::install() {
39  if (!pending_target_.IsValid()) {
40  LOG_ERROR << "Aborting target image installation; no valid target found.";
42  "Aborting target image installation; no valid target found.");
43  }
44 
45  auto target_name = pending_target_.filename();
46  auto result = installPendingTarget(pending_target_);
47 
48  switch (result.result_code.num_code) {
49  case data::ResultCode::Numeric::kOk: {
50  storage_->saveInstalledVersion(ecu_serial_.ToString(), pending_target_, InstalledVersionUpdateMode::kCurrent);
51  pending_target_ = Uptane::Target::Unknown();
52  LOG_INFO << "The target has been successfully installed: " << target_name;
53  break;
54  }
55  case data::ResultCode::Numeric::kNeedCompletion: {
56  storage_->saveInstalledVersion(ecu_serial_.ToString(), pending_target_, InstalledVersionUpdateMode::kPending);
57  LOG_INFO << "The target has been successfully installed, but a reboot is required to be applied: " << target_name;
58  break;
59  }
60  default: {
61  LOG_INFO << "Failed to install the target: " << target_name;
62  }
63  }
64 
65  return result;
66 }
67 
68 data::InstallationResult AktualizrSecondary::doFullVerification(const Metadata& metadata) {
69  // 5.4.4.2. Full verification https://uptane.github.io/uptane-standard/uptane-standard.html#metadata_verification
70 
71  // 1. Load and verify the current time or the most recent securely attested time.
72  // We trust the time that the given system/ECU provides.
73  TimeStamp now(TimeStamp::Now());
74 
75  // 2. Download and check the Root metadata file from the Director repository.
76  // 3. NOT SUPPORTED: Download and check the Timestamp metadata file from the Director repository.
77  // 4. NOT SUPPORTED: Download and check the Snapshot metadata file from the Director repository.
78  // 5. Download and check the Targets metadata file from the Director repository.
79  try {
80  director_repo_.updateMeta(*storage_, metadata);
81  } catch (const std::exception& e) {
82  LOG_ERROR << "Failed to update Director metadata: " << e.what();
84  std::string("Failed to update Director metadata: ") + e.what());
85  }
86 
87  // 6. Download and check the Root metadata file from the Image repository.
88  // 7. Download and check the Timestamp metadata file from the Image repository.
89  // 8. Download and check the Snapshot metadata file from the Image repository.
90  // 9. Download and check the top-level Targets metadata file from the Image repository.
91  try {
92  image_repo_.updateMeta(*storage_, metadata);
93  } catch (const std::exception& e) {
94  LOG_ERROR << "Failed to update Image repo metadata: " << e.what();
96  std::string("Failed to update Image repo metadata: ") + e.what());
97  }
98 
99  // 10. Verify that Targets metadata from the Director and Image repositories match.
100  if (!director_repo_.matchTargetsWithImageTargets(*(image_repo_.getTargets()))) {
101  LOG_ERROR << "Targets metadata from the Director and Image repositories do not match";
103  "Targets metadata from the Director and Image repositories do not match");
104  }
105 
106  auto targetsForThisEcu = director_repo_.getTargets(serial(), hwID());
107 
108  if (targetsForThisEcu.size() != 1) {
109  LOG_ERROR << "Invalid number of targets (should be 1): " << targetsForThisEcu.size();
112  "Invalid number of targets (should be 1): " + std::to_string(targetsForThisEcu.size()));
113  }
114 
115  if (!isTargetSupported(targetsForThisEcu[0])) {
116  LOG_ERROR << "The given target type is not supported: " << targetsForThisEcu[0].type();
118  "The given target type is not supported: " + targetsForThisEcu[0].type());
119  }
120 
121  pending_target_ = targetsForThisEcu[0];
122 
123  LOG_DEBUG << "Metadata verified, new update found.";
124  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
125 }
126 
127 void AktualizrSecondary::uptaneInitialize() {
128  if (keys_->generateUptaneKeyPair().size() == 0) {
129  throw std::runtime_error("Failed to generate Uptane key pair");
130  }
131 
132  // from uptane/initialize.cc but we only take care of our own serial/hwid
133  EcuSerials ecu_serials;
134 
135  if (storage_->loadEcuSerials(&ecu_serials)) {
136  ecu_serial_ = ecu_serials[0].first;
137  hardware_id_ = ecu_serials[0].second;
138  return;
139  }
140 
141  std::string ecu_serial_local = config_.uptane.ecu_serial;
142  if (ecu_serial_local.empty()) {
143  ecu_serial_local = keys_->UptanePublicKey().KeyId();
144  }
145 
146  std::string ecu_hardware_id = config_.uptane.ecu_hardware_id;
147  if (ecu_hardware_id.empty()) {
148  ecu_hardware_id = Utils::getHostname();
149  if (ecu_hardware_id == "") {
150  throw std::runtime_error("Failed to define ECU hardware ID");
151  }
152  }
153 
154  ecu_serials.emplace_back(Uptane::EcuSerial(ecu_serial_local), Uptane::HardwareIdentifier(ecu_hardware_id));
155  storage_->storeEcuSerials(ecu_serials);
156  ecu_serial_ = ecu_serials[0].first;
157  hardware_id_ = ecu_serials[0].second;
158 
159  // this is a way to find out and store a value of the target name that is installed
160  // at the initial/provisioning stage and included into a device manifest
161  // i.e. 'filepath' field or ["signed"]["installed_image"]["filepath"]
162  // this value must match the value pushed to the backend during the bitbaking process,
163  // specifically, at its OSTree push phase and is equal to
164  // GARAGE_TARGET_NAME ?= "${OSTREE_BRANCHNAME}" which in turn is equal to OSTREE_BRANCHNAME ?= "${SOTA_HARDWARE_ID}"
165  // therefore, by default GARAGE_TARGET_NAME == OSTREE_BRANCHNAME == SOTA_HARDWARE_ID
166  // If there is no match then the backend/UI will not render/highlight currently installed version at all/correctly
167  storage_->importInstalledVersions(config_.import.base_path);
168 }
169 
170 void AktualizrSecondary::initPendingTargetIfAny() {
171  try {
172  director_repo_.checkMetaOffline(*storage_);
173  } catch (const std::exception& e) {
174  LOG_INFO << "No valid metadata found in storage.";
175  return;
176  }
177 
178  auto targetsForThisEcu = director_repo_.getTargets(ecu_serial_, hardware_id_);
179 
180  if (targetsForThisEcu.size() != 1) {
181  LOG_ERROR << "Invalid number of targets (should be 1): " << targetsForThisEcu.size();
182  return;
183  }
184 
185  if (!isTargetSupported(targetsForThisEcu[0])) {
186  LOG_ERROR << "The given target type is not supported: " << targetsForThisEcu[0].type();
187  return;
188  }
189 
190  pending_target_ = targetsForThisEcu[0];
191 }
192 
193 void AktualizrSecondary::registerHandlers() {
194  registerHandler(AKIpUptaneMes_PR_getInfoReq,
195  std::bind(&AktualizrSecondary::getInfoHdlr, this, std::placeholders::_1, std::placeholders::_2));
196 
197  registerHandler(AKIpUptaneMes_PR_versionReq,
198  std::bind(&AktualizrSecondary::versionHdlr, this, std::placeholders::_1, std::placeholders::_2));
199 
200  registerHandler(AKIpUptaneMes_PR_manifestReq,
201  std::bind(&AktualizrSecondary::getManifestHdlr, this, std::placeholders::_1, std::placeholders::_2));
202 
203  registerHandler(AKIpUptaneMes_PR_putMetaReq2,
204  std::bind(&AktualizrSecondary::putMetaHdlr, this, std::placeholders::_1, std::placeholders::_2));
205 
206  registerHandler(AKIpUptaneMes_PR_installReq,
207  std::bind(&AktualizrSecondary::installHdlr, this, std::placeholders::_1, std::placeholders::_2));
208 }
209 
210 MsgHandler::ReturnCode AktualizrSecondary::getInfoHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
211  (void)in_msg;
212 
213  out_msg.present(AKIpUptaneMes_PR_getInfoResp);
214  auto info_resp = out_msg.getInfoResp();
215 
216  SetString(&info_resp->ecuSerial, serial().ToString());
217  SetString(&info_resp->hwId, hwID().ToString());
218  info_resp->keyType = static_cast<AKIpUptaneKeyType_t>(publicKey().Type());
219  SetString(&info_resp->key, publicKey().Value());
220 
221  return ReturnCode::kOk;
222 }
223 
224 MsgHandler::ReturnCode AktualizrSecondary::versionHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
225  const uint32_t version = 2;
226  auto version_req = in_msg.versionReq();
227  const auto primary_version = static_cast<uint32_t>(version_req->version);
228  if (primary_version < version) {
229  LOG_ERROR << "Primary protocol version is " << primary_version << " but Secondary version is " << version
230  << "! Communication will most likely fail!";
231  } else if (primary_version > version) {
232  LOG_INFO << "Primary protocol version is " << primary_version << " but Secondary version is " << version
233  << ". Please consider upgrading the Secondary.";
234  }
235 
236  out_msg.present(AKIpUptaneMes_PR_versionResp);
237  auto version_resp = out_msg.versionResp();
238  version_resp->version = version;
239 
240  return ReturnCode::kOk;
241 }
242 
243 AktualizrSecondary::ReturnCode AktualizrSecondary::getManifestHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
244  (void)in_msg;
245 
246  out_msg.present(AKIpUptaneMes_PR_manifestResp);
247  auto manifest_resp = out_msg.manifestResp();
248  manifest_resp->manifest.present = manifest_PR_json;
249  SetString(&manifest_resp->manifest.choice.json, Utils::jsonToStr(getManifest())); // NOLINT
250 
251  LOG_TRACE << "Manifest: \n" << getManifest();
252  return ReturnCode::kOk;
253 }
254 
255 void AktualizrSecondary::copyMetadata(Uptane::MetaBundle& meta_bundle, const Uptane::RepositoryType repo,
256  const Uptane::Role& role, std::string& json) {
257  auto key = std::make_pair(repo, role);
258  if (meta_bundle.count(key) > 0) {
259  LOG_WARNING << repo.toString() << " metadata in contains multiple " << role.ToString() << " objects.";
260  return;
261  }
262  meta_bundle.emplace(key, std::move(json));
263 }
264 
265 AktualizrSecondary::ReturnCode AktualizrSecondary::putMetaHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
266  auto md = in_msg.putMetaReq2();
267  Uptane::MetaBundle meta_bundle;
268 
269  if (md->directorRepo.present == directorRepo_PR_collection) {
270  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
271  const int director_meta_count = md->directorRepo.choice.collection.list.count;
272  for (int i = 0; i < director_meta_count; i++) {
273  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-union-access)
274  const AKMetaJson_t object = *md->directorRepo.choice.collection.list.array[i];
275  const std::string role = ToString(object.role);
276  std::string json = ToString(object.json);
277  LOG_DEBUG << "Received Director repo " << role << " metadata:\n" << json;
278  if (role == Uptane::Role::ROOT) {
279  copyMetadata(meta_bundle, Uptane::RepositoryType::Director(), Uptane::Role::Root(), json);
280  } else if (role == Uptane::Role::TARGETS) {
281  copyMetadata(meta_bundle, Uptane::RepositoryType::Director(), Uptane::Role::Targets(), json);
282  } else {
283  LOG_WARNING << "Director metadata in unknown format:" << md->directorRepo.present;
284  }
285  }
286  }
287 
288  if (md->imageRepo.present == imageRepo_PR_collection) {
289  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
290  const int image_meta_count = md->imageRepo.choice.collection.list.count;
291  for (int i = 0; i < image_meta_count; i++) {
292  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-union-access)
293  const AKMetaJson_t object = *md->imageRepo.choice.collection.list.array[i];
294  const std::string role = ToString(object.role);
295  std::string json = ToString(object.json);
296  LOG_DEBUG << "Received Image repo " << role << " metadata:\n" << json;
297  if (role == Uptane::Role::ROOT) {
298  copyMetadata(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Root(), json);
299  } else if (role == Uptane::Role::TIMESTAMP) {
300  copyMetadata(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp(), json);
301  } else if (role == Uptane::Role::SNAPSHOT) {
302  copyMetadata(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot(), json);
303  } else if (role == Uptane::Role::TARGETS) {
304  copyMetadata(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Targets(), json);
305  } else {
306  LOG_WARNING << "Image metadata in unknown format:" << md->imageRepo.present;
307  }
308  }
309  }
310 
311  if (meta_bundle.size() != 6) {
312  LOG_WARNING << "Metadata received from Primary is incomplete: " << md->imageRepo.present;
313  }
314 
315  data::InstallationResult result = putMetadata(meta_bundle);
316 
317  auto m = out_msg.present(AKIpUptaneMes_PR_putMetaResp2).putMetaResp2();
318  m->result = static_cast<AKInstallationResultCode_t>(result.result_code.num_code);
319  SetString(&m->description, result.description);
320 
321  return ReturnCode::kOk;
322 }
323 
324 AktualizrSecondary::ReturnCode AktualizrSecondary::installHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
325  (void)in_msg;
326  auto result = install();
327 
328  auto m = out_msg.present(AKIpUptaneMes_PR_installResp2).installResp2();
329  m->result = static_cast<AKInstallationResultCode_t>(result.result_code.num_code);
330  SetString(&m->description, result.description);
331 
332  if (data::ResultCode::Numeric::kNeedCompletion == result.result_code.num_code) {
333  return ReturnCode::kRebootRequired;
334  }
335 
336  return ReturnCode::kOk;
337 }
STL namespace.
TUF Roles.
Definition: tuf.h:61
Reference counted holder for the top-level ASN1 message structure.
Definition: asn1_message.h:34
SWM Internal integrity error.
Results of libaktualizr API calls.
Definition: results.h:12