Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
managedsecondary.cc
1 #include "managedsecondary.h"
2 
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 
7 #include <boost/algorithm/hex.hpp>
8 #include <boost/filesystem.hpp>
9 
10 #include "crypto/crypto.h"
11 #include "logging/logging.h"
12 #include "uptane/manifest.h"
13 #include "uptane/uptanerepository.h"
14 #include "utilities/exceptions.h"
15 #include "utilities/fault_injection.h"
16 #include "utilities/utils.h"
17 
18 namespace Primary {
19 
20 ManagedSecondary::ManagedSecondary(Primary::ManagedSecondaryConfig sconfig_in) : sconfig(std::move(sconfig_in)) {
21  loadMetadata();
22  std::string public_key_string;
23 
24  if (!loadKeys(&public_key_string, &private_key)) {
25  if (!Crypto::generateKeyPair(sconfig.key_type, &public_key_string, &private_key)) {
26  LOG_ERROR << "Could not generate rsa keys for secondary " << ManagedSecondary::getSerial() << "@"
27  << sconfig.ecu_hardware_id;
28  throw std::runtime_error("Unable to generate secondary rsa keys");
29  }
30 
31  // do not store keys yet, wait until SotaUptaneClient performed device initialization
32  }
33  public_key_ = PublicKey(public_key_string, sconfig.key_type);
34  Initialize();
35 }
36 
37 void ManagedSecondary::Initialize() {
38  struct stat st {};
39 
40  if (!boost::filesystem::is_directory(sconfig.metadata_path)) {
41  Utils::createDirectories(sconfig.metadata_path, S_IRWXU);
42  }
43  if (stat(sconfig.metadata_path.c_str(), &st) < 0) {
44  throw std::runtime_error(std::string("Could not check metadata directory permissions: ") + std::strerror(errno));
45  }
46  if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
47  throw std::runtime_error("Secondary metadata directory has unsafe permissions");
48  }
49 
50  if (!boost::filesystem::is_directory(sconfig.full_client_dir)) {
51  Utils::createDirectories(sconfig.full_client_dir, S_IRWXU);
52  }
53  if (stat(sconfig.full_client_dir.c_str(), &st) < 0) {
54  throw std::runtime_error(std::string("Could not check client directory permissions: ") + std::strerror(errno));
55  }
56  if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
57  throw std::runtime_error("Secondary client directory has unsafe permissions");
58  }
59 
60  storeKeys(public_key_.Value(), private_key);
61 }
62 
63 void ManagedSecondary::rawToMeta() {
64  // raw meta is trusted
65  current_meta.director_root = Uptane::Root(
66  Uptane::RepositoryType::Director(),
67  Utils::parseJSON(getMetaFromBundle(meta_bundle_, Uptane::RepositoryType::Director(), Uptane::Role::Root())));
68  current_meta.director_targets = Uptane::Targets(
69  Utils::parseJSON(getMetaFromBundle(meta_bundle_, Uptane::RepositoryType::Director(), Uptane::Role::Targets())));
70  current_meta.image_root = Uptane::Root(
71  Uptane::RepositoryType::Image(),
72  Utils::parseJSON(getMetaFromBundle(meta_bundle_, Uptane::RepositoryType::Image(), Uptane::Role::Root())));
73  current_meta.image_timestamp = Uptane::TimestampMeta(
74  Utils::parseJSON(getMetaFromBundle(meta_bundle_, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp())));
75  current_meta.image_snapshot = Uptane::Snapshot(
76  Utils::parseJSON(getMetaFromBundle(meta_bundle_, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot())));
77  current_meta.image_targets = Uptane::Targets(
78  Utils::parseJSON(getMetaFromBundle(meta_bundle_, Uptane::RepositoryType::Image(), Uptane::Role::Targets())));
79 }
80 
81 data::InstallationResult ManagedSecondary::putMetadata(const Uptane::Target &target) {
82  Uptane::MetaBundle temp_bundle;
83  if (!secondary_provider_->getMetadata(&temp_bundle, target)) {
85  "Unable to load stored metadata from Primary");
86  }
87 
88  // No verification is currently performed, we can add verification in future for testing purposes
89  detected_attack = "";
90 
91  meta_bundle_ = std::move(temp_bundle);
92  rawToMeta(); // meta_bundle_ -> current_meta
93  if (!current_meta.isConsistent()) {
95  "Error verifying metadata received from Primary");
96  }
97  storeMetadata();
98 
99  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
100 }
101 
102 int ManagedSecondary::getRootVersion(const bool director) const {
103  if (director) {
104  return current_meta.director_root.version();
105  }
106  return current_meta.image_root.version();
107 }
108 
109 data::InstallationResult ManagedSecondary::putRoot(const std::string &root, const bool director) {
110  const Uptane::RepositoryType repo = (director) ? Uptane::RepositoryType::Director() : Uptane::RepositoryType::Image();
111  Uptane::Root &prev_root = (director) ? current_meta.director_root : current_meta.image_root;
112  const std::string prev_raw_root = getMetaFromBundle(meta_bundle_, repo, Uptane::Role::Root());
113  Uptane::Root new_root = Uptane::Root(repo, Utils::parseJSON(root));
114 
115  // No verification is currently performed, we can add verification in future for testing purposes
116  if (new_root.version() == prev_root.version() + 1) {
117  prev_root = new_root;
118  meta_bundle_.insert({std::make_pair(repo, Uptane::Role::Root()), root});
119  } else {
120  detected_attack = "Tried to update Root version " + std::to_string(prev_root.version()) + " with version " +
121  std::to_string(new_root.version());
122  }
123 
124  if (!current_meta.isConsistent()) {
126  }
127  storeMetadata();
128  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
129 }
130 
131 data::InstallationResult ManagedSecondary::sendFirmware(const Uptane::Target &target) {
132  (void)target;
133  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
134 }
135 
136 data::InstallationResult ManagedSecondary::install(const Uptane::Target &target) {
137  auto str = secondary_provider_->getTargetFileHandle(target);
138  std::ofstream out_file(sconfig.firmware_path.string(), std::ios::binary);
139  out_file << str.rdbuf();
140  str.close();
141  out_file.close();
142 
143  Utils::writeFile(sconfig.target_name_path, target.filename());
144  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
145 }
146 
147 Uptane::Manifest ManagedSecondary::getManifest() const {
148  Uptane::InstalledImageInfo firmware_info;
149  if (!getFirmwareInfo(firmware_info)) {
150  return Json::Value(Json::nullValue);
151  }
152 
153  Json::Value manifest = Uptane::ManifestIssuer::assembleManifest(firmware_info, getSerial());
154  // consider updating Uptane::ManifestIssuer functionality to fulfill the given use-case
155  // and removing the following code from here so we encapsulate manifest generation
156  // and signing functionality in one place
157  manifest["attacks_detected"] = detected_attack;
158 
159  Json::Value signed_ecu_version;
160 
161  std::string b64sig = Utils::toBase64(Crypto::RSAPSSSign(nullptr, private_key, Utils::jsonToCanonicalStr(manifest)));
162  Json::Value signature;
163  signature["method"] = "rsassa-pss";
164  signature["sig"] = b64sig;
165 
166  signature["keyid"] = public_key_.KeyId();
167  signed_ecu_version["signed"] = manifest;
168  signed_ecu_version["signatures"] = Json::Value(Json::arrayValue);
169  signed_ecu_version["signatures"].append(signature);
170 
171  return signed_ecu_version;
172 }
173 
174 bool ManagedSecondary::getFirmwareInfo(Uptane::InstalledImageInfo &firmware_info) const {
175  std::string content;
176 
177  if (!boost::filesystem::exists(sconfig.target_name_path) || !boost::filesystem::exists(sconfig.firmware_path)) {
178  firmware_info.name = std::string("noimage");
179  content = "";
180  } else {
181  firmware_info.name = Utils::readFile(sconfig.target_name_path.string());
182  content = Utils::readFile(sconfig.firmware_path.string());
183  }
184  firmware_info.hash = Uptane::ManifestIssuer::generateVersionHashStr(content);
185  firmware_info.len = content.size();
186 
187  return true;
188 }
189 
190 void ManagedSecondary::storeKeys(const std::string &pub_key, const std::string &priv_key) {
191  Utils::writeFile((sconfig.full_client_dir / sconfig.ecu_private_key), priv_key);
192  Utils::writeFile((sconfig.full_client_dir / sconfig.ecu_public_key), pub_key);
193 }
194 
195 bool ManagedSecondary::loadKeys(std::string *pub_key, std::string *priv_key) {
196  boost::filesystem::path public_key_path = sconfig.full_client_dir / sconfig.ecu_public_key;
197  boost::filesystem::path private_key_path = sconfig.full_client_dir / sconfig.ecu_private_key;
198 
199  if (!boost::filesystem::exists(public_key_path) || !boost::filesystem::exists(private_key_path)) {
200  return false;
201  }
202 
203  *priv_key = Utils::readFile(private_key_path.string());
204  *pub_key = Utils::readFile(public_key_path.string());
205  return true;
206 }
207 
208 bool MetaPack::isConsistent() const {
209  TimeStamp now(TimeStamp::Now());
210  try {
211  if (director_root.original() != Json::nullValue) {
212  Uptane::Root original_root(director_root);
213  Uptane::Root new_root(Uptane::RepositoryType::Director(), director_root.original(), new_root);
214  if (director_targets.original() != Json::nullValue) {
215  Uptane::Targets(Uptane::RepositoryType::Director(), Uptane::Role::Targets(), director_targets.original(),
216  std::make_shared<Uptane::MetaWithKeys>(original_root));
217  }
218  }
219  } catch (const std::logic_error &exc) {
220  LOG_WARNING << "Inconsistent metadata: " << exc.what();
221  return false;
222  }
223  return true;
224 }
225 
226 } // namespace Primary
STL namespace.
SWM Internal integrity error.