]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
import: rework how verification works
authorLennart Poettering <lennart@poettering.net>
Fri, 15 Jan 2021 15:45:29 +0000 (16:45 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 19 Jan 2021 17:29:59 +0000 (18:29 +0100)
Previously the PullJob object took internal care of rerequested the
SHA256SUMS file, if requesting <image>.sha256 didn't work. This was a
weird a non-abstraction only used when actually getting the checksum
files.

Let's move this out of the PullJob, so that it is generic again, and
does roughly the same stuff for all resources it is used for: let's
define a generic .on_not_found() handler that can be set on a PullJob
object, and is called whenever with see HTTP 404, and may be used to
provide a new URL to try if the first didn't work.

This is also preparation for later work to support PKCS#7 signatures
instead of gpg signatures, where a similar logic is needed, and we thus
should have a generic infrastructure place.

This gets rid of the VerificationStyle field in the PullJob object:
instead of storing this non-generic field we just derive the same
information from the URL itself, which is safe, since we generated it
ourselves earlier.

src/import/pull-common.c
src/import/pull-common.h
src/import/pull-job.c
src/import/pull-job.h
src/import/pull-raw.c
src/import/pull-tar.c

index 4631431707c2988d1570ccc3dee2b0f0a98bffed..faa988b5f2a5000700ab2663c5a88f6167f9803c 100644 (file)
@@ -374,6 +374,7 @@ int pull_verify(PullJob *main_job,
         char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX";
         _cleanup_(sigkill_waitp) pid_t pid = 0;
         bool gpg_home_created = false;
+        VerificationStyle style;
         int r;
 
         assert(main_job);
@@ -406,7 +407,13 @@ int pull_verify(PullJob *main_job,
         if (!signature_job)
                 return 0;
 
-        if (checksum_job->style == VERIFICATION_PER_FILE)
+        r = verification_style_from_url(checksum_job->url, &style);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine verification style from URL '%s': %m", checksum_job->url);
+
+        if (style == VERIFICATION_PER_FILE) /* if this is per-file verification, then the signature is in the
+                                             * checksum job, let's thus ignore the signature job from now on,
+                                             * and use the checksum job as signature job. */
                 signature_job = checksum_job;
 
         assert(signature_job->state == PULL_JOB_DONE);
@@ -480,7 +487,7 @@ int pull_verify(PullJob *main_job,
                         cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
 
                 cmd[k++] = "--verify";
-                if (checksum_job->style == VERIFICATION_PER_DIRECTORY) {
+                if (style == VERIFICATION_PER_DIRECTORY) {
                         cmd[k++] = sig_file_path;
                         cmd[k++] = "-";
                         cmd[k++] = NULL;
@@ -522,3 +529,55 @@ finish:
 
         return r;
 }
+
+int verification_style_from_url(const char *url, VerificationStyle *ret) {
+        _cleanup_free_ char *last = NULL;
+        int r;
+
+        assert(url);
+        assert(ret);
+
+        /* Determines which kind of verification style is appropriate for this url */
+
+        r = import_url_last_component(url, &last);
+        if (r < 0)
+                return r;
+
+        if (streq(last, "SHA256SUMS")) {
+                *ret = VERIFICATION_PER_DIRECTORY;
+                return 0;
+        }
+
+        if (endswith(last, ".sha256")) {
+                *ret = VERIFICATION_PER_FILE;
+                return 0;
+        }
+
+        return -EINVAL;
+}
+
+int pull_job_restart_with_sha256sum(PullJob *j, char **ret) {
+        VerificationStyle style;
+        int r;
+
+        assert(j);
+
+        /* Generic implementation of a PullJobNotFound handler, that restarts the job requesting SHA256SUMS */
+
+        r = verification_style_from_url(j->url, &style);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine verification style of URL '%s': %m", j->url);
+
+        if (style == VERIFICATION_PER_DIRECTORY) /* Nothing to do anymore */
+                return 0;
+
+        assert(style == VERIFICATION_PER_FILE); /* This must have been .sha256 style URL before */
+
+        log_debug("Got 404 for %s, now trying to get SHA256SUMS instead.", j->url);
+
+        r = import_url_change_last_component(j->url, "SHA256SUMS", ret);
+        if (r < 0)
+                return log_error_errno(r, "Failed to replace SHA256SUMS suffix: %m");
+
+        return 1;
+}
index 025bcee2bd70168fa0b28058ba6ea874c560d453..a83e9b7e1456d1262ce22dddfa56480e14f6567c 100644 (file)
@@ -16,3 +16,14 @@ int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes
 int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
 
 int pull_verify(PullJob *main_job, PullJob *roothash_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job);
+
+typedef enum VerificationStyle {
+        VERIFICATION_PER_FILE,      /* SuSE-style ".sha256" files with inline gpg signature */
+        VERIFICATION_PER_DIRECTORY, /* Ubuntu-style SHA256SUM files with detached SHA256SUM.gpg signatures */
+        _VERIFICATION_STYLE_MAX,
+        _VERIFICATION_STYLE_INVALID = -1,
+} VerificationStyle;
+
+int verification_style_from_url(const char *url, VerificationStyle *style);
+
+int pull_job_restart_with_sha256sum(PullJob *job, char **ret);
index eea00380a4918c0468133f669c8f0f9517bdb53c..2f6bca8336503e77d6871d30803b21fb4d612a6c 100644 (file)
@@ -61,16 +61,16 @@ static void pull_job_finish(PullJob *j, int ret) {
                 j->on_finished(j);
 }
 
-static int pull_job_restart(PullJob *j) {
+static int pull_job_restart(PullJob *j, const char *new_url) {
         int r;
-        char *chksum_url = NULL;
 
-        r = import_url_change_last_component(j->url, "SHA256SUMS", &chksum_url);
+        assert(j);
+        assert(new_url);
+
+        r = free_and_strdup(&j->url, new_url);
         if (r < 0)
                 return r;
 
-        free(j->url);
-        j->url = chksum_url;
         j->state = PULL_JOB_INIT;
         j->payload = mfree(j->payload);
         j->payload_size = 0;
@@ -114,23 +114,31 @@ 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 (status == 404 && j->on_not_found) {
+                        _cleanup_free_ char *new_url = NULL;
+
+                        /* This resource wasn't found, but the implementor wants to maybe let us know a new URL, query for it. */
+                        r = j->on_not_found(j, &new_url);
                         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 (r > 0) { /* A new url to use */
+                                assert(new_url);
+
+                                r = pull_job_restart(j, new_url);
+                                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;
+                                if (status == 0)
+                                        return;
                         }
                 }
 
@@ -556,7 +564,6 @@ int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata)
                 .start_usec = now(CLOCK_MONOTONIC),
                 .compressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
                 .uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
-                .style = VERIFICATION_STYLE_UNSET,
                 .url = TAKE_PTR(u),
         };
 
index 70ef62699ab4be3728855cffbf9ea7af0966bf11..48cc773beb62bb351e3262404db71b9a3fc25500 100644 (file)
@@ -13,6 +13,7 @@ typedef void (*PullJobFinished)(PullJob *job);
 typedef int (*PullJobOpenDisk)(PullJob *job);
 typedef int (*PullJobHeader)(PullJob *job, const char *header, size_t sz);
 typedef void (*PullJobProgress)(PullJob *job);
+typedef int (*PullJobNotFound)(PullJob *job, char **ret_new_url);
 
 typedef enum PullJobState {
         PULL_JOB_INIT,
@@ -24,12 +25,6 @@ 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 {
@@ -43,6 +38,7 @@ struct PullJob {
         PullJobOpenDisk on_open_disk;
         PullJobHeader on_header;
         PullJobProgress on_progress;
+        PullJobNotFound on_not_found;
 
         CurlGlue *glue;
         CURL *curl;
@@ -79,8 +75,6 @@ struct PullJob {
         gcry_md_hd_t checksum_context;
 
         char *checksum;
-
-        VerificationStyle style;
 };
 
 int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata);
index 4a2bab3d0734433dcaa2efaaa598f107a680b754..69b3c307f62bcef592ff3a814aee4ab394816a50 100644 (file)
@@ -473,11 +473,23 @@ static void raw_pull_job_on_finished(PullJob *j) {
         if (!raw_pull_is_done(i))
                 return;
 
-        if (i->signature_job && 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?)");
+        if (i->signature_job && i->signature_job->error != 0) {
+                VerificationStyle style;
 
-                r = i->signature_job->error;
-                goto finish;
+                r = verification_style_from_url(i->checksum_job->url, &style);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
+                        goto finish;
+                }
+
+                if (style == VERIFICATION_PER_DIRECTORY) { /* A failed signature file download only matters
+                                                            * in per-directory verification mode, since only
+                                                            * then the signature is detached, and thus a file
+                                                            * of its own. */
+                        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)
@@ -722,7 +734,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;
+                i->checksum_job->on_not_found = pull_job_restart_with_sha256sum;
 
                 r = pull_job_begin(i->checksum_job);
                 if (r < 0)
index b646d38539e6af17933a0d78628e20ecc22d4e37..728b0fe68556d12e71378d4b07a1fdc280d49171 100644 (file)
@@ -302,11 +302,23 @@ static void tar_pull_job_on_finished(PullJob *j) {
         if (!tar_pull_is_done(i))
                 return;
 
-        if (i->signature_job && 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?)");
+        if (i->signature_job && i->signature_job->error != 0) {
+                VerificationStyle style;
 
-                r = i->signature_job->error;
-                goto finish;
+                r = verification_style_from_url(i->checksum_job->url, &style);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
+                        goto finish;
+                }
+
+                if (style == VERIFICATION_PER_DIRECTORY) { /* A failed signature file download only matters
+                                                            * in per-directory verification mode, since only
+                                                            * then the signature is detached, and thus a file
+                                                            * of its own. */
+                        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);
@@ -540,7 +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;
+                i->checksum_job->on_not_found = pull_job_restart_with_sha256sum;
 
                 r = pull_job_begin(i->checksum_job);
                 if (r < 0)