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