3rd-party: Import certificate from 3rd-party repository on repo add

When trying to add the repository for the first time, download the public
certificate published by the 3rd party repository and with a user confirmation,
use it to install the 3rd-party os-core. After that the official certificate will
be installed for future commands.

Signed-off-by: Otavio Pontes <otavio.pontes@intel.com>
This commit is contained in:
Otavio Pontes
2020-02-21 13:17:04 -08:00
parent 5553124356
commit 8fdb10efd2
2 changed files with 252 additions and 0 deletions
+152
View File
@@ -19,9 +19,12 @@
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "3rd_party_repos.h"
#include "signature.h"
#include "swupd.h"
#ifdef THIRDPARTY
@@ -89,6 +92,138 @@ static int remove_repo(const char *repo_name)
return 0;
}
static bool confirm_certificate(const char *cert)
{
int c;
info("Importing 3rd-party repository public certificate:\n\n");
signature_print_info(cert);
info("\n");
info("Do you want to accept this certificate? (y/N): ");
c = tolower(getchar());
info("\n");
return c == 'y';
}
static int import_temp_certificate(int version, char *hash)
{
char *cert_tar, *cert, *url;
int ret = 0;
cert_tar = sys_path_join(globals.state_dir, "temp_cert.tar");
cert = sys_path_join(globals.state_dir, hash);
url = str_or_die("%s/%d/files/%s.tar", globals.content_url, version, hash);
ret = swupd_curl_get_file(url, cert_tar);
if (ret != 0) {
goto out;
}
ret = archives_check_single_file_tarball(cert_tar, hash);
if (ret != 0) {
goto out;
}
ret = archives_extract_to(cert_tar, globals.state_dir);
if (ret != 0) {
goto out;
}
if (confirm_certificate(cert)) {
signature_deinit();
if (!signature_init(cert, NULL)) {
signature_deinit();
ret = -EPERM;
goto out;
}
} else {
ret = -EACCES;
goto out;
}
out:
unlink(cert_tar);
unlink(cert);
free(cert);
free(cert_tar);
free(url);
return ret;
}
static int import_certificate_from_version(int version)
{
int ret = 0;
char *os_core, *url, *os_core_tar;
struct manifest *manifest = NULL;
struct list *i;
struct file *cert_file = NULL;
os_core_tar = sys_path_join(globals.state_dir, "temp_manifest.tar");
os_core = sys_path_join(globals.state_dir, "Manifest.os-core");
url = str_or_die("%s/%d/%s", globals.content_url, version, "Manifest.os-core.tar");
unlink(os_core_tar);
ret = swupd_curl_get_file(url, os_core_tar);
if (ret) {
goto out;
}
ret = archives_extract_to(os_core_tar, globals.state_dir);
if (ret != 0) {
goto out;
}
manifest = manifest_parse("os-core", os_core, false);
if (!manifest) {
ret = -EPROTO;
goto out;
}
for (i = manifest->files; i; i = i->next) {
struct file *f = i->data;
if (strncmp(f->filename, CERT_PATH, sizeof(CERT_PATH)) == 0) {
cert_file = f;
break;
}
}
if (!cert_file) {
ret = -ENOENT;
goto out;
}
ret = import_temp_certificate(cert_file->last_change, cert_file->hash);
out:
manifest_free(manifest);
unlink(os_core_tar);
unlink(os_core);
free(os_core);
free(os_core_tar);
free(url);
return ret;
}
static int import_third_party_certificate(void)
{
char *url;
int tmp_version;
url = str_or_die("%s/version/format%s/latest", globals.content_url, globals.format_string);
tmp_version = get_int_from_url(url);
free(url);
if (tmp_version <= 0) {
return -1;
}
return import_certificate_from_version(tmp_version);
}
enum swupd_code third_party_add_main(int argc, char **argv)
{
enum swupd_code ret_code = SWUPD_OK;
@@ -158,6 +293,23 @@ enum swupd_code third_party_add_main(int argc, char **argv)
goto finish;
}
if (globals.sigcheck && !globals.user_defined_cert_path) {
ret = import_third_party_certificate();
if (ret < 0) {
if (ret == -ENOENT) {
error("Public certificate not found on 3rd-party repository\n");
info("To ignore certificate check use --nosigcheck\n");
} else if (ret != -EACCES) {
error("Impossible to import 3rd party repository certificate\n");
info("To ignore certificate check use --nosigcheck\n");
}
revert = true;
ret_code = SWUPD_BAD_CERT;
goto finish;
}
}
/* get repo's latest version */
repo_version = get_latest_version(repo->url);
if (repo_version < 0) {
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env bats
# Author: Otavio Pontes
# Email: otavio.pontes@intel.com
load "../testlib"
export repo1
export repo2
global_setup() {
# Skip this test for local development because we write the certificate on /
# This is necessary because the default certificate location is hardcoded on build time and we
# need to run swupd without -C parameter to test this feature.
if [ -z "${RUNNING_IN_CI}" ]; then
return
fi
if [ ! -f /usr/share/clear/update-ca/Swupd_Root.pem ]; then
sudo mkdir -p /usr/share/clear/update-ca
sudo cp "$TEST_ROOT_DIR"/Swupd_Root.pem /usr/share/clear/update-ca
export CERT_WAS_INSTALLED=1
fi
}
global_teardown() {
if [ -z "${RUNNING_IN_CI}" ]; then
return
fi
if [ ! -z "${CERT_WAS_INSTALLED}" ]; then
sudo rm usr/share/clear/update-ca/Swupd_Root.pem
fi
}
test_setup() {
create_test_environment "$TEST_NAME"
create_third_party_repo "$TEST_NAME" 10 staging test-repo1
repo1="$TPWEBDIR"
create_third_party_repo "$TEST_NAME" 10 staging test-repo2
repo2="$TPWEBDIR"
}
@test "TPR066: Add a single repo importing the certificate" {
run sudo sh -c "echo 'y' | $SWUPD 3rd-party add test-repo1 file://$repo1 $SWUPD_OPTS_NO_CERT"
assert_status_is "$SWUPD_OK"
expected_output=$(cat <<-EOM
Adding 3rd-party repository test-repo1...
Importing 3rd-party repository public certificate:
Issuer: /C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org/CN=localhost
Subject: /C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org/CN=localhost
.*
Installing the required bundle 'os-core' from the repository...
Note that all bundles added from a 3rd-party repository are forced to run with the --no-scripts flag for security reasons
Loading required manifests...
Downloading packs for:
- os-core
Finishing packs extraction...
Validate downloaded files
No extra files need to be downloaded
Installing files...
Warning: post-update helper scripts skipped due to --no-scripts argument
Successfully installed 1 bundle
Repository added successfully
EOM
)
assert_regex_is_output "$expected_output"
run sudo sh -c "cat $STATEDIR/3rd-party/repo.ini"
expected_output=$(cat <<-EOM
[test-repo1]
url=file://$repo1
EOM
)
assert_is_output "$expected_output"
# make sure the os-core bundle of the repo is installed in the appropriate place
assert_file_exists "$TARGETDIR"/"$THIRD_PARTY_BUNDLES_DIR"/test-repo1/usr/share/clear/bundles/os-core
assert_file_exists "$TARGETDIR"/"$THIRD_PARTY_BUNDLES_DIR"/test-repo1/usr/lib/os-release
}
@test "TPR067: Reject a certificate when adding a repository" {
run sudo sh -c "echo 'n' | $SWUPD 3rd-party add test-repo1 file://$repo1 $SWUPD_OPTS_NO_CERT"
assert_status_is "$SWUPD_BAD_CERT"
expected_output=$(cat <<-EOM
Adding 3rd-party repository test-repo1...
Importing 3rd-party repository public certificate:
Issuer: /C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org/CN=localhost
Subject: /C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org/CN=localhost
.*
Failed to add repository
EOM
)
assert_regex_is_output "$expected_output"
}