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  : config_(std::move(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_INFO << "Metadata verified, new update found.";
124  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
125 }
126 
127 void AktualizrSecondary::uptaneInitialize() {
128  if (keys_->generateUptaneKeyPair().empty()) {
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.empty()) {
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, 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) const {
211  (void)in_msg;
212  LOG_INFO << "Received an information request message; sending requested information.";
213 
214  out_msg.present(AKIpUptaneMes_PR_getInfoResp);
215  auto info_resp = out_msg.getInfoResp();
216 
217  SetString(&info_resp->ecuSerial, serial().ToString());
218  SetString(&info_resp->hwId, hwID().ToString());
219  info_resp->keyType = static_cast<AKIpUptaneKeyType_t>(publicKey().Type());
220  SetString(&info_resp->key, publicKey().Value());
221 
222  return ReturnCode::kOk;
223 }
224 
225 MsgHandler::ReturnCode AktualizrSecondary::versionHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
226  const uint32_t version = 2;
227  auto version_req = in_msg.versionReq();
228  const auto primary_version = static_cast<uint32_t>(version_req->version);
229  if (primary_version < version) {
230  LOG_ERROR << "Primary protocol version is " << primary_version << " but Secondary version is " << version
231  << "! Communication will most likely fail!";
232  } else if (primary_version > version) {
233  LOG_INFO << "Primary protocol version is " << primary_version << " but Secondary version is " << version
234  << ". Please consider upgrading the Secondary.";
235  }
236 
237  out_msg.present(AKIpUptaneMes_PR_versionResp);
238  auto version_resp = out_msg.versionResp();
239  version_resp->version = version;
240 
241  return ReturnCode::kOk;
242 }
243 
244 AktualizrSecondary::ReturnCode AktualizrSecondary::getManifestHdlr(Asn1Message& in_msg, Asn1Message& out_msg) const {
245  (void)in_msg;
246  if (last_msg_ != AKIpUptaneMes_PR_manifestReq) {
247  LOG_INFO << "Received a manifest request message; sending requested manifest.";
248  } else {
249  LOG_DEBUG << "Received another manifest request message; sending the same manifest.";
250  }
251 
252  out_msg.present(AKIpUptaneMes_PR_manifestResp);
253  auto manifest_resp = out_msg.manifestResp();
254  manifest_resp->manifest.present = manifest_PR_json;
255  SetString(&manifest_resp->manifest.choice.json, Utils::jsonToStr(getManifest())); // NOLINT
256 
257  LOG_TRACE << "Manifest: \n" << getManifest();
258  return ReturnCode::kOk;
259 }
260 
261 void AktualizrSecondary::copyMetadata(Uptane::MetaBundle& meta_bundle, const Uptane::RepositoryType repo,
262  const Uptane::Role& role, std::string& json) {
263  auto key = std::make_pair(repo, role);
264  if (meta_bundle.count(key) > 0) {
265  LOG_WARNING << repo.toString() << " metadata in contains multiple " << role.ToString() << " objects.";
266  return;
267  }
268  meta_bundle.emplace(key, std::move(json));
269 }
270 
271 AktualizrSecondary::ReturnCode AktualizrSecondary::putMetaHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
272  LOG_INFO << "Received a put metadata request message; verifying contents...";
273  auto md = in_msg.putMetaReq2();
274  Uptane::MetaBundle meta_bundle;
275 
276  if (md->directorRepo.present == directorRepo_PR_collection) {
277  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
278  const int director_meta_count = md->directorRepo.choice.collection.list.count;
279  for (int i = 0; i < director_meta_count; i++) {
280  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-union-access)
281  const AKMetaJson_t object = *md->directorRepo.choice.collection.list.array[i];
282  const std::string role = ToString(object.role);
283  std::string json = ToString(object.json);
284  LOG_DEBUG << "Received Director repo " << role << " metadata:\n" << json;
285  if (role == Uptane::Role::ROOT) {
286  copyMetadata(meta_bundle, Uptane::RepositoryType::Director(), Uptane::Role::Root(), json);
287  } else if (role == Uptane::Role::TARGETS) {
288  copyMetadata(meta_bundle, Uptane::RepositoryType::Director(), Uptane::Role::Targets(), json);
289  } else {
290  LOG_WARNING << "Director metadata in unknown format:" << md->directorRepo.present;
291  }
292  }
293  }
294 
295  if (md->imageRepo.present == imageRepo_PR_collection) {
296  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
297  const int image_meta_count = md->imageRepo.choice.collection.list.count;
298  for (int i = 0; i < image_meta_count; i++) {
299  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-union-access)
300  const AKMetaJson_t object = *md->imageRepo.choice.collection.list.array[i];
301  const std::string role = ToString(object.role);
302  std::string json = ToString(object.json);
303  LOG_DEBUG << "Received Image repo " << role << " metadata:\n" << json;
304  if (role == Uptane::Role::ROOT) {
305  copyMetadata(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Root(), json);
306  } else if (role == Uptane::Role::TIMESTAMP) {
307  copyMetadata(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp(), json);
308  } else if (role == Uptane::Role::SNAPSHOT) {
309  copyMetadata(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot(), json);
310  } else if (role == Uptane::Role::TARGETS) {
311  copyMetadata(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Targets(), json);
312  } else {
313  LOG_WARNING << "Image metadata in unknown format:" << md->imageRepo.present;
314  }
315  }
316  }
317 
318  if (meta_bundle.size() != 6) {
319  LOG_WARNING << "Metadata received from Primary is incomplete: " << md->imageRepo.present;
320  }
321 
322  data::InstallationResult result = putMetadata(meta_bundle);
323 
324  auto m = out_msg.present(AKIpUptaneMes_PR_putMetaResp2).putMetaResp2();
325  m->result = static_cast<AKInstallationResultCode_t>(result.result_code.num_code);
326  SetString(&m->description, result.description);
327 
328  return ReturnCode::kOk;
329 }
330 
331 AktualizrSecondary::ReturnCode AktualizrSecondary::installHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
332  (void)in_msg;
333  LOG_INFO << "Received an installation request message; attempting installation...";
334  auto result = install();
335 
336  auto m = out_msg.present(AKIpUptaneMes_PR_installResp2).installResp2();
337  m->result = static_cast<AKInstallationResultCode_t>(result.result_code.num_code);
338  SetString(&m->description, result.description);
339 
340  if (data::ResultCode::Numeric::kNeedCompletion == result.result_code.num_code) {
341  return ReturnCode::kRebootRequired;
342  }
343 
344  return ReturnCode::kOk;
345 }
data::ResultCode::Numeric::kVerificationFailed
@ kVerificationFailed
Metadata verification failed.
KeyManager
Definition: keymanager.h:13
data::InstallationResult
Definition: types.h:277
AktualizrSecondaryConfig
Definition: aktualizr_secondary_config.h:35
Uptane::HardwareIdentifier
Definition: types.h:315
TimeStamp
Definition: types.h:188
Uptane::RepositoryType
Definition: tuf.h:21
Uptane::InstalledImageInfo
Definition: types.h:306
Uptane::EcuSerial
Definition: types.h:346
PublicKey
Definition: types.h:119
result
Results of libaktualizr API calls.
Definition: results.h:12
data::ResultCode::Numeric::kInternalError
@ kInternalError
SWM Internal integrity error.
Asn1Message
Reference counted holder for the top-level ASN1 message structure.
Definition: asn1_message.h:34
Uptane::Role
TUF Roles.
Definition: tuf.h:61
Metadata
Definition: aktualizr_secondary_metadata.h:7
Uptane::Manifest
Definition: types.h:448