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 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 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 BasedPath& import_pubkey_path,
51  const 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 = 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  sql_storage.storeMisconfiguredEcus(ecus);
176  }
177 
178  std::vector<Uptane::Target> installed_versions;
179  size_t current_index = SIZE_MAX;
180  size_t k = 0;
181  fs_storage.loadInstalledVersions(&installed_versions, &current_index);
182  for (auto it = installed_versions.cbegin(); it != installed_versions.cend(); it++, k++) {
183  auto mode = k == current_index ? InstalledVersionUpdateMode::kCurrent : InstalledVersionUpdateMode::kNone;
184  sql_storage.savePrimaryInstalledVersion(*it, mode);
185  }
186 
187  // migrate latest versions of all metadata
188  for (const auto& role : Uptane::Role::Roles()) {
189  if (role == Uptane::Role::Root()) {
190  continue;
191  }
192 
193  std::string meta;
194  for (auto repo : {Uptane::RepositoryType::Director(), Uptane::RepositoryType::Image()}) {
195  if (fs_storage.loadNonRoot(&meta, repo, role)) {
196  sql_storage.storeNonRoot(meta, repo, role);
197  }
198  }
199  }
200  // additionally migrate the whole root metadata chain
201  std::string latest_root;
202  for (auto repo : {Uptane::RepositoryType::Director(), Uptane::RepositoryType::Image()}) {
203  if (fs_storage.loadLatestRoot(&latest_root, Uptane::RepositoryType::Director())) {
204  int latest_version = Uptane::extractVersionUntrusted(latest_root);
205  for (int version = 0; version <= latest_version; ++version) {
206  std::string root;
207  if (fs_storage.loadRoot(&root, repo, Uptane::Version(version))) {
208  sql_storage.storeRoot(root, repo, Uptane::Version(version));
209  }
210  }
211  }
212  }
213 
214  // if everything is ok, remove old files.
215  fs_storage.cleanUpAll();
216 }
217 
218 bool INvStorage::fsReadInstalledVersions(const boost::filesystem::path& filename,
219  std::vector<Uptane::Target>* installed_versions, size_t* current_version) {
220  std::string current_hash;
221  if (access(filename.c_str(), R_OK) != 0) {
222  return false;
223  }
224  const Json::Value installed_versions_json = Utils::parseJSONFile(filename.string());
225  std::vector<Uptane::Target> new_versions;
226  size_t k = 0;
227  for (auto it = installed_versions_json.begin(); it != installed_versions_json.end(); ++it, ++k) {
228  if (!(*it).isObject()) {
229  // We loaded old format, migrate to new one.
230  Json::Value t_json;
231  t_json["hashes"]["sha256"] = it.key();
232  Uptane::Target t((*it).asString(), t_json);
233  new_versions.push_back(t);
234  if (current_version != nullptr) {
235  *current_version = k;
236  }
237  } else {
238  if (current_version != nullptr && (*it)["is_current"].asBool()) {
239  *current_version = k;
240  }
241  Uptane::Target t(it.key().asString(), *it);
242  new_versions.push_back(t);
243  }
244  }
245  *installed_versions = new_versions;
246 
247  return true;
248 }
Uptane::Version
Metadata version numbers.
Definition: tuf.h:116
StorageException
Definition: storage_exception.h:4
BasedPath
Definition: utils.h:101
StorageConfig
Definition: storage_config.h:15
Uptane::Target
Definition: tuf.h:238
SQLStorage
Definition: sqlstorage.h:18
FSStorageRead
Definition: fsstorage_read.h:7
ImportConfig
Definition: storage_config.h:34