Skip to content
This repository has been archived by the owner on May 21, 2024. It is now read-only.

Basic first-order delegation support #1074

Merged
merged 19 commits into from
Feb 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Our versioning scheme is `YEAR.N` where `N` is incremented whenever a new releas

## [??? (unreleased)]

### Added

- Basic first-order delegation support.

## [2019.1] - 2019-01-10

### Changed
Expand Down
6 changes: 6 additions & 0 deletions actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ These are the primary actions that a user of libaktualizr can perform through th
- [x] Send UpdateCheckComplete event after successful check with no available updates (aktualizr_test.cc)
- [x] Send UpdateCheckComplete event after failure (aktualizr_test.cc)
- [x] Download updates
- [x] Find requested target
- [x] Search first-order delegations (uptane_delegation_test.cc)
- [x] Download an update
- [x] Download an OSTree package (fetcher_test.cc)
- [x] Download a binary package (uptane_vector_tests.cc, aktualizr_test.cc)
Expand Down Expand Up @@ -178,6 +180,9 @@ These are internal requirements that are relatively opaque to the user and/or co
- [x] Sign TUF metadata
- [x] Sign TUF metadata with RSA2048 (keymanager_test.cc)
- [x] Sign TUF metadata with ED25519 (keymanager_test.cc)
- [x] Validate TUF roles (tuf_test.cc)
- [x] Delegated roles have custom names (tuf_test.cc)
- [x] Reject delegated role names that are identical to reserved role names (tuf_test.cc)
- [x] Validate a TUF root (tuf_test.cc, uptane_test.cc)
- [x] Throw an exception if a TUF root is invalid
- [x] Throw an exception if a TUF root is unsigned (tuf_test.cc, uptane_test.cc)
Expand All @@ -188,6 +193,7 @@ These are internal requirements that are relatively opaque to the user and/or co
- [x] Parse Uptane timestamps (types_test.cc)
- [x] Throw an exception if an Uptane timestamp is invalid (types_test.cc)
- [x] Get current time (types_test.cc)
- [x] Validate first-order target delegations (uptane_delegation_test.cc)
- [x] Reject http GET responses that exceed size limit (httpclient_test.cc)
- [x] Reject http GET responses that do not meet speed limit (httpclient_test.cc)
- [x] Abort update if any signature threshold is <= 0 (REQ-153, uptane_vector_tests.cc)
Expand Down
1 change: 1 addition & 0 deletions config/sql/migration/migrate.15.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
-- Don't modify this! Create a new migration instead--see docs/schema-migrations.adoc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

SAVEPOINT MIGRATION;

CREATE TABLE rollback_migrations(version_from INT PRIMARY KEY, migration TEXT NOT NULL);
Expand Down
1 change: 1 addition & 0 deletions config/sql/migration/migrate.16.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
-- Don't modify this! Create a new migration instead--see docs/schema-migrations.adoc
SAVEPOINT MIGRATION;

DROP TABLE installation_result;
Expand Down
9 changes: 9 additions & 0 deletions config/sql/migration/migrate.17.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Don't modify this! Create a new migration instead--see docs/schema-migrations.adoc
SAVEPOINT MIGRATION;

CREATE TABLE delegations(meta BLOB NOT NULL, role_name TEXT NOT NULL, UNIQUE(role_name));

DELETE FROM version;
INSERT INTO version VALUES(17);

RELEASE MIGRATION;
9 changes: 9 additions & 0 deletions config/sql/rollback/rollback.17.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Don't modify this! Create a new migration instead--see docs/schema-migrations.adoc
SAVEPOINT ROLLBACK_MIGRATION;

DROP TABLE delegations;

DELETE FROM version;
INSERT INTO version VALUES(16);

RELEASE ROLLBACK_MIGRATION;
3 changes: 2 additions & 1 deletion config/sql/schema.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CREATE TABLE version(version INTEGER);
INSERT INTO version(rowid,version) VALUES(1,16);
INSERT INTO version(rowid,version) VALUES(1,17);
CREATE TABLE device_info(unique_mark INTEGER PRIMARY KEY CHECK (unique_mark = 0), device_id TEXT, is_registered INTEGER NOT NULL DEFAULT 0 CHECK (is_registered IN (0,1)));
CREATE TABLE ecu_serials(serial TEXT UNIQUE, hardware_id TEXT NOT NULL, is_primary INTEGER NOT NULL CHECK (is_primary IN (0,1)));
CREATE TABLE misconfigured_ecus(serial TEXT UNIQUE, hardware_id TEXT NOT NULL, state INTEGER NOT NULL CHECK (state IN (0,1)));
Expand All @@ -23,3 +23,4 @@ CREATE TABLE device_installation_result(unique_mark INTEGER PRIMARY KEY CHECK (u
CREATE TABLE ecu_installation_results(ecu_serial TEXT NOT NULL PRIMARY KEY, success INTEGER NOT NULL DEFAULT 0, result_code TEXT NOT NULL DEFAULT "", description TEXT NOT NULL DEFAULT "");
CREATE TABLE need_reboot(unique_mark INTEGER PRIMARY KEY CHECK (unique_mark = 0), flag INTEGER NOT NULL DEFAULT 0);
CREATE TABLE rollback_migrations(version_from INT PRIMARY KEY, migration TEXT NOT NULL);
CREATE TABLE delegations(meta BLOB NOT NULL, role_name TEXT NOT NULL, UNIQUE(role_name));
6 changes: 3 additions & 3 deletions src/aktualizr_repo/image_repo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ void ImageRepo::addDelegation(const Uptane::Role &name, const std::string &path,
if (keys_.count(name) != 0) {
throw std::runtime_error("Delegation with the same name already exist.");
}
if (Delegation::isBadName(name.ToString())) {
throw std::runtime_error("Delegation with the wrong name, this name is reserved.");
if (Uptane::Role::IsReserved(name.ToString())) {
throw std::runtime_error("Delegation name " + name.ToString() + " is reserved.");
}
generateKeyPair(key_type, name);
Json::Value delegate;
Expand Down Expand Up @@ -83,4 +83,4 @@ void ImageRepo::addCustomImage(const std::string &name, const Uptane::Hash &hash
target["hashes"]["sha512"] = hash.HashString();
}
addImage(name, target, delegation);
}
}
4 changes: 2 additions & 2 deletions src/aktualizr_repo/repo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Json::Value Repo::getTarget(const std::string &target_name) {
return image_targets["targets"][target_name];
} else {
for (auto &p : boost::filesystem::directory_iterator(path_ / "repo" / repo_type_.toString())) {
if (!Delegation::isBadName(p.path().stem().string())) {
if (!Uptane::Role::IsReserved(p.path().stem().string())) {
auto targets = Utils::parseJSONFile(p)["signed"];
if (targets["targets"].isMember(target_name)) {
return targets["targets"][target_name];
Expand All @@ -224,7 +224,7 @@ void Repo::readKeys() {
key_type_str >> key_type;
std::string private_key_string(Utils::readFile(p / "private.key"));
auto name = p.path().filename().string();
keys_[Uptane::Role(name, !Delegation::isBadName(name))] =
keys_[Uptane::Role(name, !Uptane::Role::IsReserved(name))] =
KeyPair(PublicKey(public_key_string, key_type), private_key_string);
}
}
8 changes: 2 additions & 6 deletions src/aktualizr_repo/repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ struct KeyPair {
struct Delegation {
Delegation() = default;
Delegation(const boost::filesystem::path &repo_path, std::string delegation_name) : name(std::move(delegation_name)) {
if (isBadName(name)) {
throw std::runtime_error("Delegation with the wrong name, this name is reserved.");
if (Uptane::Role::IsReserved(name)) {
throw std::runtime_error("Delegation name " + name + " is reserved.");
}
boost::filesystem::path delegation_path(((repo_path / "repo/image") / name).string() + ".json");
boost::filesystem::path targets_path(repo_path / "repo/image/targets.json");
Expand All @@ -44,10 +44,6 @@ struct Delegation {
bool isMatched(const boost::filesystem::path &image_path) {
return (fnmatch(pattern.c_str(), image_path.c_str(), 0) == 0);
}
static bool isBadName(const std::string &delegation_name) {
return (delegation_name == "root" || delegation_name == "targets" || delegation_name == "snapshot" ||
delegation_name == "timestamp");
}
operator bool() const { return (!name.empty() && !pattern.empty()); }
std::string name;
std::string pattern;
Expand Down
4 changes: 2 additions & 2 deletions src/aktualizr_repo/repo_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ TEST(aktualizr_repo, sign) {
auto json = Utils::parseJSON(output);
Uptane::Root root(Uptane::RepositoryType::Director(),
Utils::parseJSONFile(temp_dir.Path() / "repo/director/root.json"));
root.UnpackSignedObject(Uptane::RepositoryType::Director(), json);
EXPECT_NO_THROW(root.UnpackSignedObject(Uptane::RepositoryType::Director(), json));
root.UnpackSignedObject(Uptane::RepositoryType::Director(), Uptane::Role::Snapshot(), json);
EXPECT_NO_THROW(root.UnpackSignedObject(Uptane::RepositoryType::Director(), Uptane::Role::Snapshot(), json));
check_repo(temp_dir);
}

Expand Down
3 changes: 2 additions & 1 deletion src/aktualizr_secondary/aktualizr_secondary.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ bool AktualizrSecondary::putMetadataResp(const Uptane::RawMetaPack& meta_pack) {

// TODO: proper partial verification
root_ = Uptane::Root(Uptane::RepositoryType::Director(), Utils::parseJSON(meta_pack.director_root), root_);
Uptane::Targets targets(Uptane::RepositoryType::Director(), Utils::parseJSON(meta_pack.director_targets), root_);
Uptane::Targets targets(Uptane::RepositoryType::Director(), Uptane::Role::Targets(),
Utils::parseJSON(meta_pack.director_targets), std::make_shared<Uptane::Root>(root_));
if (meta_targets_.version() > targets.version()) {
detected_attack_ = "Rollback attack detected";
return true;
Expand Down
5 changes: 3 additions & 2 deletions src/aktualizr_secondary/opcuaserver_secondary_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ void OpcuaServerSecondaryDelegate::handleAllMetaDataFilesReceived(opcuabridge::S
// TODO: proper root metadata rotation
secondary_->root_ = Uptane::Root(Uptane::RepositoryType::Director(),
Utils::parseJSON(received_meta_pack_.director_root), secondary_->root_);
Uptane::Targets targets(Uptane::RepositoryType::Director(), Utils::parseJSON(received_meta_pack_.director_targets),
secondary_->root_);
Uptane::Targets targets(Uptane::RepositoryType::Director(), Uptane::Role::Targets(),
Utils::parseJSON(received_meta_pack_.director_targets),
std::make_shared<Uptane::MetaWithKeys>(secondary_->root_));
if (secondary_->meta_targets_.version() > targets.version()) {
secondary_->detected_attack_ = "Rollback attack detected";
LOG_ERROR << "Uptane security check: " << secondary_->detected_attack_;
Expand Down
2 changes: 1 addition & 1 deletion src/libaktualizr/package_manager/packagemanagerfake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Uptane::Target PackageManagerFake::getCurrent() const {

data::InstallationResult PackageManagerFake::install(const Uptane::Target &target) const {
// fault injection: only enabled with FIU_ENABLE defined
if (fiu_fail("fake_package_install")) {
if (fiu_fail("fake_package_install") != 0) {
std::string failure_cause = fault_injection_get_parameter("fault_fake_package_install_cause");
if (failure_cause.empty()) {
failure_cause = "Installation failed";
Expand Down
1 change: 1 addition & 0 deletions src/libaktualizr/primary/aktualizr.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class Aktualizr {
FRIEND_TEST(Aktualizr, UpdateCheckCompleteError);
FRIEND_TEST(Aktualizr, PauseResumeEvents);
FRIEND_TEST(Aktualizr, AddSecondary);
FRIEND_TEST(Delegation, Basic);

// This constructor is only being used in tests
Aktualizr(Config& config, std::shared_ptr<INvStorage> storage_in, std::shared_ptr<HttpInterface> http_in);
Expand Down
37 changes: 12 additions & 25 deletions src/libaktualizr/primary/aktualizr_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,6 @@

boost::filesystem::path uptane_repos_dir;

Config makeTestConfig(const TemporaryDirectory& temp_dir, const std::string& url) {
Config conf("tests/config/basic.toml");
conf.uptane.director_server = url + "/director";
conf.uptane.repo_server = url + "/repo";
conf.provision.server = url;
conf.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
conf.provision.primary_ecu_hardware_id = "primary_hw";
conf.storage.path = temp_dir.Path();
conf.tls.server = url;
UptaneTestCommon::addDefaultSecondary(conf, temp_dir, "secondary_ecu_serial", "secondary_hw");
return conf;
}

void verifyNothingInstalled(const Json::Value& manifest) {
// Verify nothing has installed for the primary.
EXPECT_EQ(
Expand Down Expand Up @@ -96,7 +83,7 @@ TEST(Aktualizr, FullNoUpdates) {
future_FullNoUpdates = promise_FullNoUpdates.get_future();
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFake>(temp_dir.Path(), "noupdates");
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
Expand All @@ -122,7 +109,7 @@ TEST(Aktualizr, FullNoUpdates) {
TEST(Aktualizr, AddSecondary) {
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFake>(temp_dir.Path(), "noupdates");
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
Expand Down Expand Up @@ -296,7 +283,7 @@ TEST(Aktualizr, FullWithUpdates) {
future_FullWithUpdates = promise_FullWithUpdates.get_future();
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFakeEventCounter>(temp_dir.Path());
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
Expand Down Expand Up @@ -366,7 +353,7 @@ class HttpFakePutCounter : public HttpFake {
TEST(Aktualizr, FullWithUpdatesNeedReboot) {
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFakePutCounter>(temp_dir.Path());
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
conf.pacman.fake_need_reboot = true;
conf.bootloader.reboot_sentinel_dir = temp_dir.Path();

Expand Down Expand Up @@ -578,7 +565,7 @@ TEST(Aktualizr, CheckNoUpdates) {
future_CheckNoUpdates = promise_CheckNoUpdates.get_future();
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFake>(temp_dir.Path(), "noupdates");
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
Expand Down Expand Up @@ -672,7 +659,7 @@ TEST(Aktualizr, DownloadWithUpdates) {
future_DownloadWithUpdates = promise_DownloadWithUpdates.get_future();
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates");
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
Expand Down Expand Up @@ -804,7 +791,7 @@ TEST(Aktualizr, InstallWithUpdates) {
future_InstallWithUpdates = promise_InstallWithUpdates.get_future();
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates");
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
Expand Down Expand Up @@ -907,7 +894,7 @@ void CampaignCheck_events(const std::shared_ptr<event::BaseEvent>& event) {
TEST(Aktualizr, CampaignCheckAndAccept) {
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFakeCampaign>(temp_dir.Path());
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

{
auto storage = INvStorage::newStorage(conf.storage);
Expand Down Expand Up @@ -954,7 +941,7 @@ TEST(Aktualizr, FullNoCorrelationId) {
// scope `Aktualizr` object, so that the ReportQueue flushes its events before
// we count them at the end
{
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
Expand Down Expand Up @@ -1008,7 +995,7 @@ TEST(Aktualizr, APICheck) {
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates");

Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);

Expand Down Expand Up @@ -1058,7 +1045,7 @@ TEST(Aktualizr, UpdateCheckCompleteError) {
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpPutManifestFail>(temp_dir.Path());

Config conf = makeTestConfig(temp_dir, "http://updatefail");
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, "http://updatefail");

auto storage = INvStorage::newStorage(conf.storage);
CountUpdateCheckEvents counter;
Expand All @@ -1075,7 +1062,7 @@ TEST(Aktualizr, UpdateCheckCompleteError) {
TEST(Aktualizr, PauseResumeEvents) {
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFake>(temp_dir.Path(), "noupdates");
Config conf = makeTestConfig(temp_dir, http->tls_server);
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
Expand Down
Loading