importd: support SUSE style checksums (#5206)
In order to verify a pulled container or disk image, importd only supports SHA256SUMS files with the detached signature in SHA256SUMS.gpg. SUSE is using an inline signed file with the name of the image itself and the suffix .sha256 instead. This commit adds support for this type of signature files. It is first attempted to pull the .sha256 file. If this fails with error 404, the SHA256SUMS and SHA256SUMS.gpg files are pulled and used for verification.
This commit is contained in:
parent
8ea9aa9e88
commit
697be0be15
|
@ -713,19 +713,22 @@
|
|||
is automatically derived from the last component of the URL,
|
||||
with its suffix removed.</para>
|
||||
|
||||
<para>The image is verified before it is made available,
|
||||
unless <option>--verify=no</option> is specified. Verification
|
||||
is done via SHA256SUMS and SHA256SUMS.gpg files that need to
|
||||
be made available on the same web server, under the same URL
|
||||
as the <filename>.tar</filename> file, but with the last
|
||||
component (the filename) of the URL replaced. With
|
||||
<option>--verify=checksum</option>, only the SHA256 checksum
|
||||
for the file is verified, based on the
|
||||
<filename>SHA256SUMS</filename> file. With
|
||||
<option>--verify=signature</option>, the SHA256SUMS file is
|
||||
first verified with detached GPG signature file
|
||||
<filename>SHA256SUMS.gpg</filename>. The public key for this
|
||||
verification step needs to be available in
|
||||
<para>The image is verified before it is made available, unless
|
||||
<option>--verify=no</option> is specified.
|
||||
Verification is done either via an inline signed file with the name
|
||||
of the image and the suffix <filename>.sha256</filename> or via
|
||||
separate <filename>SHA256SUMS</filename> and
|
||||
<filename>SHA256SUMS.gpg</filename> files.
|
||||
The signature files need to be made available on the same web
|
||||
server, under the same URL as the <filename>.tar</filename> file.
|
||||
With <option>--verify=checksum</option>, only the SHA256 checksum
|
||||
for the file is verified, based on the <filename>.sha256</filename>
|
||||
suffixed file or the<filename>SHA256SUMS</filename> file.
|
||||
With <option>--verify=signature</option>, the sha checksum file is
|
||||
first verified with the inline signature in the
|
||||
<filename>.sha256</filename> file or the detached GPG signature file
|
||||
<filename>SHA256SUMS.gpg</filename>.
|
||||
The public key for this verification step needs to be available in
|
||||
<filename>/usr/lib/systemd/import-pubring.gpg</filename> or
|
||||
<filename>/etc/systemd/import-pubring.gpg</filename>.</para>
|
||||
|
||||
|
|
|
@ -275,6 +275,7 @@ int pull_make_verification_jobs(
|
|||
|
||||
_cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL;
|
||||
int r;
|
||||
const char *chksums = NULL;
|
||||
|
||||
assert(ret_checksum_job);
|
||||
assert(ret_signature_job);
|
||||
|
@ -284,10 +285,16 @@ int pull_make_verification_jobs(
|
|||
assert(glue);
|
||||
|
||||
if (verify != IMPORT_VERIFY_NO) {
|
||||
_cleanup_free_ char *checksum_url = NULL;
|
||||
_cleanup_free_ char *checksum_url = NULL, *fn = NULL;
|
||||
|
||||
/* Queue job for the SHA256SUMS file for the image */
|
||||
r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url);
|
||||
/* Queue jobs for the checksum file for the image. */
|
||||
r = import_url_last_component(url, &fn);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
chksums = strjoina(fn, ".sha256");
|
||||
|
||||
r = import_url_change_last_component(url, chksums, &checksum_url);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -362,6 +369,15 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
|
|||
line,
|
||||
strlen(line));
|
||||
|
||||
if (!p) {
|
||||
line = strjoina(job->checksum, " ", fn, "\n");
|
||||
|
||||
p = memmem(checksum_job->payload,
|
||||
checksum_job->payload_size,
|
||||
line,
|
||||
strlen(line));
|
||||
}
|
||||
|
||||
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
|
||||
log_error("DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn);
|
||||
return -EBADMSG;
|
||||
|
@ -416,6 +432,9 @@ int pull_verify(PullJob *main_job,
|
|||
if (!signature_job)
|
||||
return 0;
|
||||
|
||||
if (checksum_job->style == VERIFICATION_PER_FILE)
|
||||
signature_job = checksum_job;
|
||||
|
||||
assert(signature_job->state == PULL_JOB_DONE);
|
||||
|
||||
if (!signature_job->payload || signature_job->payload_size <= 0) {
|
||||
|
@ -507,9 +526,11 @@ int pull_verify(PullJob *main_job,
|
|||
cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
|
||||
|
||||
cmd[k++] = "--verify";
|
||||
cmd[k++] = sig_file_path;
|
||||
cmd[k++] = "-";
|
||||
cmd[k++] = NULL;
|
||||
if (checksum_job->style == VERIFICATION_PER_DIRECTORY) {
|
||||
cmd[k++] = sig_file_path;
|
||||
cmd[k++] = "-";
|
||||
cmd[k++] = NULL;
|
||||
}
|
||||
|
||||
stdio_unset_cloexec();
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "xattr-util.h"
|
||||
#include "pull-common.h"
|
||||
#include "import-util.h"
|
||||
|
||||
PullJob* pull_job_unref(PullJob *j) {
|
||||
if (!j)
|
||||
|
@ -73,6 +75,33 @@ static void pull_job_finish(PullJob *j, int ret) {
|
|||
j->on_finished(j);
|
||||
}
|
||||
|
||||
static int pull_job_restart(PullJob *j) {
|
||||
int r;
|
||||
char *chksum_url = NULL;
|
||||
|
||||
r = import_url_change_last_component(j->url, "SHA256SUMS", &chksum_url);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
free(j->url);
|
||||
free(j->payload);
|
||||
j->url = chksum_url;
|
||||
j->state = PULL_JOB_INIT;
|
||||
j->payload = NULL;
|
||||
j->payload_size = 0;
|
||||
j->payload_allocated = 0;
|
||||
j->written_compressed = 0;
|
||||
j->written_uncompressed = 0;
|
||||
j->written_since_last_grow = 0;
|
||||
|
||||
r = pull_job_begin(j);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
|
||||
PullJob *j = NULL;
|
||||
CURLcode code;
|
||||
|
@ -102,6 +131,26 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
|
|||
r = 0;
|
||||
goto finish;
|
||||
} else if (status >= 300) {
|
||||
if (status == 404 && j->style == VERIFICATION_PER_FILE) {
|
||||
|
||||
/* retry pull job with SHA256SUMS file */
|
||||
r = pull_job_restart(j);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
|
||||
if (code != CURLE_OK) {
|
||||
log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
|
||||
r = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
j->style = VERIFICATION_PER_DIRECTORY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log_error("HTTP request to %s failed with code %li.", j->url, status);
|
||||
r = -EIO;
|
||||
goto finish;
|
||||
|
@ -528,6 +577,7 @@ int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata)
|
|||
j->content_length = (uint64_t) -1;
|
||||
j->start_usec = now(CLOCK_MONOTONIC);
|
||||
j->compressed_max = j->uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU; /* 64GB safety limit */
|
||||
j->style = VERIFICATION_STYLE_UNSET;
|
||||
|
||||
j->url = strdup(url);
|
||||
if (!j->url)
|
||||
|
|
|
@ -42,6 +42,12 @@ typedef enum PullJobState {
|
|||
_PULL_JOB_STATE_INVALID = -1,
|
||||
} PullJobState;
|
||||
|
||||
typedef enum VerificationStyle {
|
||||
VERIFICATION_STYLE_UNSET,
|
||||
VERIFICATION_PER_FILE, /* SuSE-style ".sha256" files with inline signature */
|
||||
VERIFICATION_PER_DIRECTORY, /* Ubuntu-style SHA256SUM files with detach SHA256SUM.gpg signatures */
|
||||
} VerificationStyle;
|
||||
|
||||
#define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED))
|
||||
|
||||
struct PullJob {
|
||||
|
@ -94,6 +100,8 @@ struct PullJob {
|
|||
|
||||
bool grow_machine_directory;
|
||||
uint64_t written_since_last_grow;
|
||||
|
||||
VerificationStyle style;
|
||||
};
|
||||
|
||||
int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata);
|
||||
|
|
|
@ -478,11 +478,9 @@ static void raw_pull_job_on_finished(PullJob *j) {
|
|||
} else if (j == i->settings_job) {
|
||||
if (j->error != 0)
|
||||
log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
|
||||
} else if (j->error != 0) {
|
||||
} else if (j->error != 0 && j != i->signature_job) {
|
||||
if (j == i->checksum_job)
|
||||
log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
|
||||
else if (j == i->signature_job)
|
||||
log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
|
||||
else
|
||||
log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
|
||||
|
||||
|
@ -500,6 +498,13 @@ static void raw_pull_job_on_finished(PullJob *j) {
|
|||
if (!raw_pull_is_done(i))
|
||||
return;
|
||||
|
||||
if (i->checksum_job->style == VERIFICATION_PER_DIRECTORY && i->signature_job->error != 0) {
|
||||
log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
|
||||
|
||||
r = i->signature_job->error;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (i->roothash_job)
|
||||
i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd);
|
||||
if (i->settings_job)
|
||||
|
@ -744,6 +749,7 @@ int raw_pull_start(
|
|||
|
||||
if (i->checksum_job) {
|
||||
i->checksum_job->on_progress = raw_pull_job_on_progress;
|
||||
i->checksum_job->style = VERIFICATION_PER_FILE;
|
||||
|
||||
r = pull_job_begin(i->checksum_job);
|
||||
if (r < 0)
|
||||
|
|
|
@ -298,11 +298,9 @@ static void tar_pull_job_on_finished(PullJob *j) {
|
|||
if (j == i->settings_job) {
|
||||
if (j->error != 0)
|
||||
log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
|
||||
} else if (j->error != 0) {
|
||||
} else if (j->error != 0 && j != i->signature_job) {
|
||||
if (j == i->checksum_job)
|
||||
log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
|
||||
else if (j == i->signature_job)
|
||||
log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
|
||||
else
|
||||
log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
|
||||
|
||||
|
@ -317,6 +315,13 @@ static void tar_pull_job_on_finished(PullJob *j) {
|
|||
if (!tar_pull_is_done(i))
|
||||
return;
|
||||
|
||||
if (i->checksum_job->style == VERIFICATION_PER_DIRECTORY && i->signature_job->error != 0) {
|
||||
log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
|
||||
|
||||
r = i->signature_job->error;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd);
|
||||
if (i->settings_job)
|
||||
i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
|
||||
|
@ -547,6 +552,7 @@ int tar_pull_start(
|
|||
|
||||
if (i->checksum_job) {
|
||||
i->checksum_job->on_progress = tar_pull_job_on_progress;
|
||||
i->checksum_job->style = VERIFICATION_PER_FILE;
|
||||
|
||||
r = pull_job_begin(i->checksum_job);
|
||||
if (r < 0)
|
||||
|
|
Loading…
Reference in New Issue