Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
invstorage.cc
1 #include "invstorage.h"
2 
3 #include <unistd.h>
4 
5 #include "fsstorage_read.h"
6 #include "logging/logging.h"
7 #include "sqlstorage.h"
8 #include "utilities/utils.h"
9 
10 void INvStorage::importSimple(const boost::filesystem::path& base_path, store_data_t store_func, load_data_t load_func,
11  const utils::BasedPath& imported_data_path) {
12  if (!(this->*load_func)(nullptr) && !imported_data_path.empty()) {
13  boost::filesystem::path abs_path = imported_data_path.get(base_path);
14  if (!boost::filesystem::exists(abs_path)) {
15  LOG_ERROR << "Couldn't import data: " << abs_path << " doesn't exist.";
16  return;
17  }
18  std::string content = Utils::readFile(abs_path.string());
19  (this->*store_func)(content);
20  }
21 }
22 
23 void INvStorage::importUpdateSimple(const boost::filesystem::path& base_path, store_data_t store_func,
24  load_data_t load_func, const utils::BasedPath& imported_data_path) {
25  std::string prev_content;
26  std::string content;
27  bool update = false;
28  if (!(this->*load_func)(&prev_content)) {
29  update = true;
30  } else if (!imported_data_path.empty()) {
31  content = Utils::readFile(imported_data_path.get(base_path).string());
32  if (Crypto::sha256digest(content) != Crypto::sha256digest(prev_content)) {
33  update = true;
34  }
35  }
36 
37  if (update && !imported_data_path.empty()) {
38  boost::filesystem::path abs_path = imported_data_path.get(base_path);
39  if (!boost::filesystem::exists(abs_path)) {
40  LOG_ERROR << "Couldn't import data: " << abs_path << " doesn't exist.";
41  return;
42  }
43  if (content.empty()) {
44  content = Utils::readFile(abs_path.string());
45  }
46  (this->*store_func)(content);
47  }
48 }
49 
50 void INvStorage::importPrimaryKeys(const boost::filesystem::path& base_path, const utils::BasedPath& import_pubkey_path,
51  const utils::BasedPath& import_privkey_path) {
52  if (loadPrimaryKeys(nullptr, nullptr) || import_pubkey_path.empty() || import_privkey_path.empty()) {
53  return;
54  }
55  const boost::filesystem::path pubkey_abs_path = import_pubkey_path.get(base_path);
56  const boost::filesystem::path privkey_abs_path = import_privkey_path.get(base_path);
57  if (!boost::filesystem::exists(pubkey_abs_path)) {
58  LOG_ERROR << "Couldn't import data: " << pubkey_abs_path << " doesn't exist.";
59  return;
60  }
61  if (!boost::filesystem::exists(privkey_abs_path)) {
62  LOG_ERROR << "Couldn't import data: " << privkey_abs_path << " doesn't exist.";
63  return;
64  }
65  const std::string pub_content = Utils::readFile(pubkey_abs_path.string());
66  const std::string priv_content = Utils::readFile(privkey_abs_path.string());
67  storePrimaryKeys(pub_content, priv_content);
68 }
69 
70 void INvStorage::importInstalledVersions(const boost::filesystem::path& base_path) {
71  std::vector<Uptane::Target> installed_versions;
72  const boost::filesystem::path file_path = utils::BasedPath("installed_versions").get(base_path);
73  loadPrimaryInstallationLog(&installed_versions, false);
74  if (!installed_versions.empty()) {
75  return;
76  }
77  size_t current_index = SIZE_MAX;
78  fsReadInstalledVersions(file_path, &installed_versions, &current_index);
79  if (current_index < installed_versions.size()) {
80  // installed versions in legacy fs storage are all for primary
81  savePrimaryInstalledVersion(installed_versions[current_index], InstalledVersionUpdateMode::kCurrent);
82  boost::filesystem::remove(file_path);
83  }
84 }
85 
86 void INvStorage::importData(const ImportConfig& import_config) {
87  importPrimaryKeys(import_config.base_path, import_config.uptane_public_key_path,
88  import_config.uptane_private_key_path);
89  // root CA certificate can be updated
90  importUpdateSimple(import_config.base_path, &INvStorage::storeTlsCa, &INvStorage::loadTlsCa,
91  import_config.tls_cacert_path);
92  importSimple(import_config.base_path, &INvStorage::storeTlsCert, &INvStorage::loadTlsCert,
93  import_config.tls_clientcert_path);
94  importSimple(import_config.base_path, &INvStorage::storeTlsPkey, &INvStorage::loadTlsPkey,
95  import_config.tls_pkey_path);
96 
97  importInstalledVersions(import_config.base_path);
98 }
99 
100 std::shared_ptr<INvStorage> INvStorage::newStorage(const StorageConfig& config, const bool readonly) {
101  switch (config.type) {
102  case StorageType::kSqlite: {
103  boost::filesystem::path db_path = config.sqldb_path.get(config.path);
104  if (!boost::filesystem::exists(db_path) && FSStorageRead::FSStoragePresent(config)) {
105  if (readonly) {
106  throw StorageException(
107  "Migration from FS is not possible because the SQL database is configured to be readonly");
108  }
109 
110  LOG_INFO << "Starting FS to SQL storage migration";
111  if (access(config.path.c_str(), R_OK | W_OK | X_OK) != 0) {
112  throw StorageException(std::string("Cannot read prior filesystem configuration from ") +
113  config.path.string() + " due to insufficient permissions.");
114  }
115  StorageConfig old_config = config;
116  old_config.type = StorageType::kFileSystem;
117  old_config.path = config.path;
118 
119  auto sql_storage = std::make_shared<SQLStorage>(config, readonly);
120  FSStorageRead fs_storage(old_config);
121  INvStorage::FSSToSQLS(fs_storage, *sql_storage);
122  return sql_storage;
123  }
124  if (!boost::filesystem::exists(db_path)) {
125  LOG_INFO << "Bootstrap empty SQL storage";
126  } else {
127  LOG_INFO << "Use existing SQL storage: " << db_path;
128  }
129  return std::make_shared<SQLStorage>(config, readonly);
130  }
131  case StorageType::kFileSystem:
132  default:
133  throw std::runtime_error("FSStorage has been removed in recent versions of aktualizr, please use SQLStorage");
134  }
135 }
136 
137 void INvStorage::FSSToSQLS(FSStorageRead& fs_storage, SQLStorage& sql_storage) {
138  std::string public_key;
139  std::string private_key;
140  if (fs_storage.loadPrimaryKeys(&public_key, &private_key)) {
141  sql_storage.storePrimaryKeys(public_key, private_key);
142  }
143 
144  std::string ca;
145  if (fs_storage.loadTlsCa(&ca)) {
146  sql_storage.storeTlsCa(ca);
147  }
148 
149  std::string cert;
150  if (fs_storage.loadTlsCert(&cert)) {
151  sql_storage.storeTlsCert(cert);
152  }
153 
154  std::string pkey;
155  if (fs_storage.loadTlsPkey(&pkey)) {
156  sql_storage.storeTlsPkey(pkey);
157  }
158 
159  std::string device_id;
160  if (fs_storage.loadDeviceId(&device_id)) {
161  sql_storage.storeDeviceId(device_id);
162  }
163 
164  EcuSerials serials;
165  if (fs_storage.loadEcuSerials(&serials)) {
166  sql_storage.storeEcuSerials(serials);
167  }
168 
169  if (fs_storage.loadEcuRegistered()) {
170  sql_storage.storeEcuRegistered();
171  }
172 
173  std::vector<MisconfiguredEcu> ecus;
174  if (fs_storage.loadMisconfiguredEcus(&ecus)) {
175  for (auto& ecu : ecus) {
176  sql_storage.saveMisconfiguredEcu(ecu);
177  }
178  }
179 
180  std::vector<Uptane::Target> installed_versions;
181  size_t current_index = SIZE_MAX;
182  size_t k = 0;
183  fs_storage.loadInstalledVersions(&installed_versions, &current_index);
184  for (auto it = installed_versions.cbegin(); it != installed_versions.cend(); it++, k++) {
185  auto mode = k == current_index ? InstalledVersionUpdateMode::kCurrent : InstalledVersionUpdateMode::kNone;
186  sql_storage.savePrimaryInstalledVersion(*it, mode);
187  }
188 
189  // migrate latest versions of all metadata
190  for (const auto& role : Uptane::Role::Roles()) {
191  if (role == Uptane::Role::Root()) {
192  continue;
193  }
194 
195  std::string meta;
196  for (auto repo : {Uptane::RepositoryType::Director(), Uptane::RepositoryType::Image()}) {
197  if (fs_storage.loadNonRoot(&meta, repo, role)) {
198  sql_storage.storeNonRoot(meta, repo, role);
199  }
200  }
201  }
202  // additionally migrate the whole Root metadata chain
203  std::string latest_root;
204  for (auto repo : {Uptane::RepositoryType::Director(), Uptane::RepositoryType::Image()}) {
205  if (fs_storage.loadLatestRoot(&latest_root, Uptane::RepositoryType::Director())) {
206  int latest_version = Uptane::extractVersionUntrusted(latest_root);
207  for (int version = 0; version <= latest_version; ++version) {
208  std::string root;
209  if (fs_storage.loadRoot(&root, repo, Uptane::Version(version))) {
210  sql_storage.storeRoot(root, repo, Uptane::Version(version));
211  }
212  }
213  }
214  }
215 
216  // if everything is ok, remove old files.
217  fs_storage.cleanUpAll();
218 }
219 
220 bool INvStorage::fsReadInstalledVersions(const boost::filesystem::path& filename,
221  std::vector<Uptane::Target>* installed_versions, size_t* current_version) {
222  std::string current_hash;
223  if (access(filename.c_str(), R_OK) != 0) {
224  return false;
225  }
226  const Json::Value installed_versions_json = Utils::parseJSONFile(filename.string());
227  std::vector<Uptane::Target> new_versions;
228  size_t k = 0;
229  for (auto it = installed_versions_json.begin(); it != installed_versions_json.end(); ++it, ++k) {
230  if (!(*it).isObject()) {
231  // We loaded old format, migrate to new one.
232  Json::Value t_json;
233  t_json["hashes"]["sha256"] = it.key();
234  Uptane::Target t((*it).asString(), t_json);
235  new_versions.push_back(t);
236  if (current_version != nullptr) {
237  *current_version = k;
238  }
239  } else {
240  if (current_version != nullptr && (*it)["is_current"].asBool()) {
241  *current_version = k;
242  }
243  Uptane::Target t(it.key().asString(), *it);
244  new_versions.push_back(t);
245  }
246  }
247  *installed_versions = new_versions;
248 
249  return true;
250 }
Metadata version numbers.
Definition: tuf.h:120
The BasedPath class Can represent an absolute or relative path, only readable through the BasePath::g...
Definition: types.h:29