Skip to content

Commit 697be0b

Browse files
tblumepoettering
authored andcommitted
importd: support SUSE style checksums (systemd#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.
1 parent 8ea9aa9 commit 697be0b

File tree

6 files changed

+119
-25
lines changed

6 files changed

+119
-25
lines changed

man/machinectl.xml

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -713,19 +713,22 @@
713713
is automatically derived from the last component of the URL,
714714
with its suffix removed.</para>
715715

716-
<para>The image is verified before it is made available,
717-
unless <option>--verify=no</option> is specified. Verification
718-
is done via SHA256SUMS and SHA256SUMS.gpg files that need to
719-
be made available on the same web server, under the same URL
720-
as the <filename>.tar</filename> file, but with the last
721-
component (the filename) of the URL replaced. With
722-
<option>--verify=checksum</option>, only the SHA256 checksum
723-
for the file is verified, based on the
724-
<filename>SHA256SUMS</filename> file. With
725-
<option>--verify=signature</option>, the SHA256SUMS file is
726-
first verified with detached GPG signature file
727-
<filename>SHA256SUMS.gpg</filename>. The public key for this
728-
verification step needs to be available in
716+
<para>The image is verified before it is made available, unless
717+
<option>--verify=no</option> is specified.
718+
Verification is done either via an inline signed file with the name
719+
of the image and the suffix <filename>.sha256</filename> or via
720+
separate <filename>SHA256SUMS</filename> and
721+
<filename>SHA256SUMS.gpg</filename> files.
722+
The signature files need to be made available on the same web
723+
server, under the same URL as the <filename>.tar</filename> file.
724+
With <option>--verify=checksum</option>, only the SHA256 checksum
725+
for the file is verified, based on the <filename>.sha256</filename>
726+
suffixed file or the<filename>SHA256SUMS</filename> file.
727+
With <option>--verify=signature</option>, the sha checksum file is
728+
first verified with the inline signature in the
729+
<filename>.sha256</filename> file or the detached GPG signature file
730+
<filename>SHA256SUMS.gpg</filename>.
731+
The public key for this verification step needs to be available in
729732
<filename>/usr/lib/systemd/import-pubring.gpg</filename> or
730733
<filename>/etc/systemd/import-pubring.gpg</filename>.</para>
731734

src/import/pull-common.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ int pull_make_verification_jobs(
275275

276276
_cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL;
277277
int r;
278+
const char *chksums = NULL;
278279

279280
assert(ret_checksum_job);
280281
assert(ret_signature_job);
@@ -284,10 +285,16 @@ int pull_make_verification_jobs(
284285
assert(glue);
285286

286287
if (verify != IMPORT_VERIFY_NO) {
287-
_cleanup_free_ char *checksum_url = NULL;
288+
_cleanup_free_ char *checksum_url = NULL, *fn = NULL;
288289

289-
/* Queue job for the SHA256SUMS file for the image */
290-
r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url);
290+
/* Queue jobs for the checksum file for the image. */
291+
r = import_url_last_component(url, &fn);
292+
if (r < 0)
293+
return r;
294+
295+
chksums = strjoina(fn, ".sha256");
296+
297+
r = import_url_change_last_component(url, chksums, &checksum_url);
291298
if (r < 0)
292299
return r;
293300

@@ -362,6 +369,15 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
362369
line,
363370
strlen(line));
364371

372+
if (!p) {
373+
line = strjoina(job->checksum, " ", fn, "\n");
374+
375+
p = memmem(checksum_job->payload,
376+
checksum_job->payload_size,
377+
line,
378+
strlen(line));
379+
}
380+
365381
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
366382
log_error("DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn);
367383
return -EBADMSG;
@@ -416,6 +432,9 @@ int pull_verify(PullJob *main_job,
416432
if (!signature_job)
417433
return 0;
418434

435+
if (checksum_job->style == VERIFICATION_PER_FILE)
436+
signature_job = checksum_job;
437+
419438
assert(signature_job->state == PULL_JOB_DONE);
420439

421440
if (!signature_job->payload || signature_job->payload_size <= 0) {
@@ -507,9 +526,11 @@ int pull_verify(PullJob *main_job,
507526
cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
508527

509528
cmd[k++] = "--verify";
510-
cmd[k++] = sig_file_path;
511-
cmd[k++] = "-";
512-
cmd[k++] = NULL;
529+
if (checksum_job->style == VERIFICATION_PER_DIRECTORY) {
530+
cmd[k++] = sig_file_path;
531+
cmd[k++] = "-";
532+
cmd[k++] = NULL;
533+
}
513534

514535
stdio_unset_cloexec();
515536

src/import/pull-job.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include "string-util.h"
3030
#include "strv.h"
3131
#include "xattr-util.h"
32+
#include "pull-common.h"
33+
#include "import-util.h"
3234

3335
PullJob* pull_job_unref(PullJob *j) {
3436
if (!j)
@@ -73,6 +75,33 @@ static void pull_job_finish(PullJob *j, int ret) {
7375
j->on_finished(j);
7476
}
7577

78+
static int pull_job_restart(PullJob *j) {
79+
int r;
80+
char *chksum_url = NULL;
81+
82+
r = import_url_change_last_component(j->url, "SHA256SUMS", &chksum_url);
83+
if (r < 0)
84+
return r;
85+
86+
free(j->url);
87+
free(j->payload);
88+
j->url = chksum_url;
89+
j->state = PULL_JOB_INIT;
90+
j->payload = NULL;
91+
j->payload_size = 0;
92+
j->payload_allocated = 0;
93+
j->written_compressed = 0;
94+
j->written_uncompressed = 0;
95+
j->written_since_last_grow = 0;
96+
97+
r = pull_job_begin(j);
98+
if (r < 0)
99+
return r;
100+
101+
return 0;
102+
}
103+
104+
76105
void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
77106
PullJob *j = NULL;
78107
CURLcode code;
@@ -102,6 +131,26 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
102131
r = 0;
103132
goto finish;
104133
} else if (status >= 300) {
134+
if (status == 404 && j->style == VERIFICATION_PER_FILE) {
135+
136+
/* retry pull job with SHA256SUMS file */
137+
r = pull_job_restart(j);
138+
if (r < 0)
139+
goto finish;
140+
141+
code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
142+
if (code != CURLE_OK) {
143+
log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
144+
r = -EIO;
145+
goto finish;
146+
}
147+
148+
if (status == 0) {
149+
j->style = VERIFICATION_PER_DIRECTORY;
150+
return;
151+
}
152+
}
153+
105154
log_error("HTTP request to %s failed with code %li.", j->url, status);
106155
r = -EIO;
107156
goto finish;
@@ -528,6 +577,7 @@ int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata)
528577
j->content_length = (uint64_t) -1;
529578
j->start_usec = now(CLOCK_MONOTONIC);
530579
j->compressed_max = j->uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU; /* 64GB safety limit */
580+
j->style = VERIFICATION_STYLE_UNSET;
531581

532582
j->url = strdup(url);
533583
if (!j->url)

src/import/pull-job.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ typedef enum PullJobState {
4242
_PULL_JOB_STATE_INVALID = -1,
4343
} PullJobState;
4444

45+
typedef enum VerificationStyle {
46+
VERIFICATION_STYLE_UNSET,
47+
VERIFICATION_PER_FILE, /* SuSE-style ".sha256" files with inline signature */
48+
VERIFICATION_PER_DIRECTORY, /* Ubuntu-style SHA256SUM files with detach SHA256SUM.gpg signatures */
49+
} VerificationStyle;
50+
4551
#define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED))
4652

4753
struct PullJob {
@@ -94,6 +100,8 @@ struct PullJob {
94100

95101
bool grow_machine_directory;
96102
uint64_t written_since_last_grow;
103+
104+
VerificationStyle style;
97105
};
98106

99107
int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata);

src/import/pull-raw.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -478,11 +478,9 @@ static void raw_pull_job_on_finished(PullJob *j) {
478478
} else if (j == i->settings_job) {
479479
if (j->error != 0)
480480
log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
481-
} else if (j->error != 0) {
481+
} else if (j->error != 0 && j != i->signature_job) {
482482
if (j == i->checksum_job)
483483
log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
484-
else if (j == i->signature_job)
485-
log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
486484
else
487485
log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
488486

@@ -500,6 +498,13 @@ static void raw_pull_job_on_finished(PullJob *j) {
500498
if (!raw_pull_is_done(i))
501499
return;
502500

501+
if (i->checksum_job->style == VERIFICATION_PER_DIRECTORY && i->signature_job->error != 0) {
502+
log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
503+
504+
r = i->signature_job->error;
505+
goto finish;
506+
}
507+
503508
if (i->roothash_job)
504509
i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd);
505510
if (i->settings_job)
@@ -744,6 +749,7 @@ int raw_pull_start(
744749

745750
if (i->checksum_job) {
746751
i->checksum_job->on_progress = raw_pull_job_on_progress;
752+
i->checksum_job->style = VERIFICATION_PER_FILE;
747753

748754
r = pull_job_begin(i->checksum_job);
749755
if (r < 0)

src/import/pull-tar.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,9 @@ static void tar_pull_job_on_finished(PullJob *j) {
298298
if (j == i->settings_job) {
299299
if (j->error != 0)
300300
log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
301-
} else if (j->error != 0) {
301+
} else if (j->error != 0 && j != i->signature_job) {
302302
if (j == i->checksum_job)
303303
log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
304-
else if (j == i->signature_job)
305-
log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
306304
else
307305
log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
308306

@@ -317,6 +315,13 @@ static void tar_pull_job_on_finished(PullJob *j) {
317315
if (!tar_pull_is_done(i))
318316
return;
319317

318+
if (i->checksum_job->style == VERIFICATION_PER_DIRECTORY && i->signature_job->error != 0) {
319+
log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
320+
321+
r = i->signature_job->error;
322+
goto finish;
323+
}
324+
320325
i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd);
321326
if (i->settings_job)
322327
i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
@@ -547,6 +552,7 @@ int tar_pull_start(
547552

548553
if (i->checksum_job) {
549554
i->checksum_job->on_progress = tar_pull_job_on_progress;
555+
i->checksum_job->style = VERIFICATION_PER_FILE;
550556

551557
r = pull_job_begin(i->checksum_job);
552558
if (r < 0)

0 commit comments

Comments
 (0)