diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 4631431707..faa988b5f2 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -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; +} diff --git a/src/import/pull-common.h b/src/import/pull-common.h index 025bcee2bd..a83e9b7e14 100644 --- a/src/import/pull-common.h +++ b/src/import/pull-common.h @@ -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); diff --git a/src/import/pull-job.c b/src/import/pull-job.c index eea00380a4..2f6bca8336 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -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); - if (status == 0) { - j->style = VERIFICATION_PER_DIRECTORY; - return; + 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) + 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), }; diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 70ef62699a..48cc773beb 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -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); diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 4a2bab3d07..69b3c307f6 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -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) diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index b646d38539..728b0fe685 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -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)