diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 08a5a6192d..12d9736725 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -6,6 +6,7 @@ #include "dirent-util.h" #include "escape.h" #include "fd-util.h" +#include "hexdecoct.h" #include "io-util.h" #include "log.h" #include "memory-util.h" @@ -247,7 +248,6 @@ int pull_make_verification_jobs( PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, - const char *checksum, /* set if literal checksum verification is requested, in which case 'verify' is set to _IMPORT_VERIFY_INVALID */ const char *url, CurlGlue *glue, PullJobFinished on_finished, @@ -261,13 +261,13 @@ int pull_make_verification_jobs( assert(ret_signature_job); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); - assert((verify < 0) || !checksum); assert(url); assert(glue); /* If verification is turned off, or if the checksum to validate is already specified we don't need * to download a checksum file or signature, hence shortcut things */ - if (verify == IMPORT_VERIFY_NO || checksum) { + if (verify < 0 || /* verification already done (via literal checksum) */ + verify == IMPORT_VERIFY_NO) { /* verification turned off */ *ret_checksum_job = *ret_signature_job = NULL; return 0; } @@ -351,7 +351,7 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { return 0; assert(job->calc_checksum); - assert(job->checksum); + assert(iovec_is_set(&job->checksum)); r = import_url_last_component(job->url, &fn); if (r < 0) @@ -365,6 +365,10 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { return log_error_errno(SYNTHETIC_ERRNO(ELOOP), "Cannot verify checksum/signature files via themselves."); + _cleanup_free_ char *cs = hexmem(job->checksum.iov_base, job->checksum.iov_len); + if (!cs) + return log_oom(); + const char *p = NULL; FOREACH_STRING(separator, " *", /* separator for binary mode */ @@ -372,12 +376,12 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { " " /* non-standard separator used by linuxcontainers.org */) { _cleanup_free_ char *line = NULL; - line = strjoin(job->checksum, separator, fn, "\n"); + line = strjoin(cs, separator, fn, "\n"); if (!line) return log_oom(); - p = memmem_safe(checksum_job->payload, - checksum_job->payload_size, + p = memmem_safe(checksum_job->payload.iov_base, + checksum_job->payload.iov_len, line, strlen(line)); if (p) @@ -385,7 +389,7 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { } /* Only counts if found at beginning of a line */ - if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) + if (!p || (p != (char*) checksum_job->payload.iov_base && p[-1] != '\n')) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", fn); @@ -394,8 +398,8 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { } static int verify_gpg( - const void *payload, size_t payload_size, - const void *signature, size_t signature_size) { + const struct iovec *payload, + const struct iovec *signature) { _cleanup_close_pair_ int gpg_pipe[2] = EBADF_PAIR; _cleanup_(rm_rf_physical_and_freep) char *gpg_home = NULL; @@ -403,21 +407,21 @@ static int verify_gpg( _cleanup_(sigkill_waitp) pid_t pid = 0; int r; - assert(payload || payload_size == 0); - assert(signature || signature_size == 0); + assert(iovec_is_valid(payload)); + assert(iovec_is_valid(signature)); r = pipe2(gpg_pipe, O_CLOEXEC); if (r < 0) return log_error_errno(errno, "Failed to create pipe for gpg: %m"); - if (signature_size > 0) { + if (iovec_is_set(signature)) { _cleanup_close_ int sig_file = -EBADF; sig_file = mkostemp(sig_file_path, O_RDWR); if (sig_file < 0) return log_error_errno(errno, "Failed to create temporary file: %m"); - r = loop_write(sig_file, signature, signature_size); + r = loop_write(sig_file, signature->iov_base, signature->iov_len); if (r < 0) { log_error_errno(r, "Failed to write to temporary file: %m"); goto finish; @@ -483,10 +487,12 @@ static int verify_gpg( gpg_pipe[0] = safe_close(gpg_pipe[0]); - r = loop_write(gpg_pipe[1], payload, payload_size); - if (r < 0) { - log_error_errno(r, "Failed to write to pipe: %m"); - goto finish; + if (iovec_is_set(payload)) { + r = loop_write(gpg_pipe[1], payload->iov_base, payload->iov_len); + if (r < 0) { + log_error_errno(r, "Failed to write to pipe: %m"); + goto finish; + } } gpg_pipe[1] = safe_close(gpg_pipe[1]); @@ -503,14 +509,13 @@ static int verify_gpg( } finish: - if (signature_size > 0) + if (iovec_is_set(signature)) (void) unlink(sig_file_path); return r; } int pull_verify(ImportVerify verify, - const char *checksum, /* Verify with literal checksum */ PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, @@ -526,33 +531,13 @@ int pull_verify(ImportVerify verify, assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); - assert((verify < 0) || !checksum); assert(main_job); assert(main_job->state == PULL_JOB_DONE); - if (verify == IMPORT_VERIFY_NO) /* verification turned off */ + if (verify < 0 || /* verification already done (via literal checksum) */ + verify == IMPORT_VERIFY_NO) /* verification turned off */ return 0; - if (checksum) { - /* Verification by literal checksum */ - assert(!checksum_job); - assert(!signature_job); - assert(!settings_job); - assert(!roothash_job); - assert(!roothash_signature_job); - assert(!verity_job); - - assert(main_job->calc_checksum); - assert(main_job->checksum); - - if (!strcaseeq(checksum, main_job->checksum)) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), - "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", - main_job->url); - - return 0; - } - r = import_url_last_component(main_job->url, &fn); if (r < 0) return log_error_errno(r, "Failed to extract filename from URL '%s': %m", main_job->url); @@ -566,11 +551,11 @@ int pull_verify(ImportVerify verify, verify_job = main_job; } else { assert(main_job->calc_checksum); - assert(main_job->checksum); + assert(iovec_is_set(&main_job->checksum)); assert(checksum_job); assert(checksum_job->state == PULL_JOB_DONE); - if (!checksum_job->payload || checksum_job->payload_size <= 0) + if (!iovec_is_set(&checksum_job->payload)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Checksum is empty, cannot verify."); @@ -596,11 +581,11 @@ int pull_verify(ImportVerify verify, assert(signature_job); assert(signature_job->state == PULL_JOB_DONE); - if (!signature_job->payload || signature_job->payload_size <= 0) + if (!iovec_is_set(&signature_job->payload)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Signature is empty, cannot verify."); - return verify_gpg(verify_job->payload, verify_job->payload_size, signature_job->payload, signature_job->payload_size); + return verify_gpg(&verify_job->payload, &signature_job->payload); } int verification_style_from_url(const char *url, VerificationStyle *ret) { diff --git a/src/import/pull-common.h b/src/import/pull-common.h index 160c035a21..ffdabcb249 100644 --- a/src/import/pull-common.h +++ b/src/import/pull-common.h @@ -14,9 +14,9 @@ int pull_find_old_etags(const char *url, const char *root, int dt, const char *p int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret); int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, ImportVerify verify, CurlGlue *glue, PullJobOpenDisk on_open_disk, PullJobFinished on_finished, void *userdata); -int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *checksum, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); +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(ImportVerify verify, const char *checksum, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job); +int pull_verify(ImportVerify verify, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job); typedef enum VerificationStyle { VERIFICATION_PER_FILE, /* SUSE-style ".sha256" files with detached gpg signature */ diff --git a/src/import/pull-job.c b/src/import/pull-job.c index b457c39ed1..621a90a86e 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -20,6 +20,16 @@ #include "time-util.h" #include "xattr-util.h" +static int http_status_ok(CURLcode status) { + /* Consider all HTTP status code in the 2xx range as OK */ + return status >= 200 && status <= 299; +} + +static int http_status_etag_exists(CURLcode status) { + /* This one is special, it's triggered by our etag mgmt logic */ + return status == 304; +} + void pull_job_close_disk_fd(PullJob *j) { if (!j) return; @@ -47,8 +57,9 @@ PullJob* pull_job_unref(PullJob *j) { free(j->url); free(j->etag); strv_free(j->old_etags); - free(j->payload); - free(j->checksum); + iovec_done(&j->payload); + iovec_done(&j->checksum); + iovec_done(&j->expected_checksum); return mfree(j); } @@ -84,15 +95,16 @@ static int pull_job_restart(PullJob *j, const char *new_url) { j->state = PULL_JOB_INIT; j->error = 0; - j->payload = mfree(j->payload); - j->payload_size = 0; + iovec_done(&j->payload); j->written_compressed = 0; j->written_uncompressed = 0; j->content_length = UINT64_MAX; j->etag = mfree(j->etag); j->etag_exists = false; j->mtime = 0; - j->checksum = mfree(j->checksum); + iovec_done(&j->checksum); + iovec_done(&j->expected_checksum); + j->expected_content_length = UINT64_MAX; curl_glue_remove_and_free(j->glue, j->curl); j->curl = NULL; @@ -114,6 +126,15 @@ static int pull_job_restart(PullJob *j, const char *new_url) { return 0; } +static uint64_t pull_job_content_length_effective(PullJob *j) { + assert(j); + + if (j->expected_content_length != UINT64_MAX) + return j->expected_content_length; + + return j->content_length; +} + void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { PullJob *j = NULL; char *scheme = NULL; @@ -166,7 +187,7 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { goto finish; } - if (status == 304) { + if (http_status_etag_exists(status)) { log_info("Image already downloaded. Skipping download."); j->etag_exists = true; r = 0; @@ -214,30 +235,46 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { goto finish; } - if (j->content_length != UINT64_MAX && - j->content_length != j->written_compressed) { + uint64_t cl = pull_job_content_length_effective(j); + if (cl != UINT64_MAX && + cl != j->written_compressed) { r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Download truncated."); goto finish; } if (j->checksum_ctx) { unsigned checksum_len; - uint8_t k[EVP_MAX_MD_SIZE]; - r = EVP_DigestFinal_ex(j->checksum_ctx, k, &checksum_len); - if (r == 0) { - r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum."); - goto finish; - } - assert(checksum_len <= sizeof k); - - j->checksum = hexmem(k, checksum_len); - if (!j->checksum) { + iovec_done(&j->checksum); + j->checksum.iov_base = malloc(EVP_MAX_MD_SIZE); + if (!j->checksum.iov_base) { r = log_oom(); goto finish; } - log_debug("SHA256 of %s is %s.", j->url, j->checksum); + r = EVP_DigestFinal_ex(j->checksum_ctx, j->checksum.iov_base, &checksum_len); + if (r == 0) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum."); + goto finish; + } + assert(checksum_len <= EVP_MAX_MD_SIZE); + j->checksum.iov_len = checksum_len; + + if (DEBUG_LOGGING) { + _cleanup_free_ char *h = hexmem(j->checksum.iov_base, j->checksum.iov_len); + if (!h) { + r = log_oom(); + goto finish; + } + + log_debug("%s of %s is %s.", EVP_MD_CTX_get0_name(j->checksum_ctx), j->url, h); + } + + if (iovec_is_set(&j->expected_checksum) && + iovec_memcmp(&j->checksum, &j->expected_checksum) != 0) { + r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Checksum of downloaded resource does not match expected checksum, yikes."); + goto finish; + } } /* Do a couple of finishing disk operations, but only if we are the sole owner of the file (i.e. no @@ -343,11 +380,14 @@ static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) } if (j->disk_fd < 0 || j->force_memory) { - if (!GREEDY_REALLOC(j->payload, j->payload_size + sz)) + uint8_t *a = j->payload.iov_base; + + if (!GREEDY_REALLOC(a, j->payload.iov_len + sz + 1)) return log_oom(); - memcpy(j->payload + j->payload_size, p, sz); - j->payload_size += sz; + *((uint8_t*) mempcpy(a + j->payload.iov_len, p, sz)) = 0; + j->payload.iov_base = a; + j->payload.iov_len += sz; } j->written_uncompressed += sz; @@ -359,38 +399,39 @@ finish: return 0; } -static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) { +static int pull_job_write_compressed(PullJob *j, const struct iovec *data) { int r; assert(j); - assert(p); + assert(iovec_is_valid(data)); - if (sz <= 0) + if (!iovec_is_set(data)) return 0; - if (j->written_compressed + sz < j->written_compressed) + if (j->written_compressed + data->iov_len < j->written_compressed) return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow"); - if (j->written_compressed + sz > j->compressed_max) + if (j->written_compressed + data->iov_len > j->compressed_max) return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing."); - if (j->content_length != UINT64_MAX && - j->written_compressed + sz > j->content_length) + uint64_t cl = pull_job_content_length_effective(j); + if (cl != UINT64_MAX && + j->written_compressed + data->iov_len > cl) return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "Content length incorrect."); if (j->checksum_ctx) { - r = EVP_DigestUpdate(j->checksum_ctx, p, sz); + r = EVP_DigestUpdate(j->checksum_ctx, data->iov_base, data->iov_len); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Could not hash chunk."); } - r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j); + r = import_uncompress(&j->compress, data->iov_base, data->iov_len, pull_job_write_uncompressed, j); if (r < 0) return r; - j->written_compressed += sz; + j->written_compressed += data->iov_len; return 0; } @@ -431,14 +472,11 @@ static int pull_job_open_disk(PullJob *j) { } static int pull_job_detect_compression(PullJob *j) { - _cleanup_free_ uint8_t *stub = NULL; - size_t stub_size; - int r; assert(j); - r = import_uncompress_detect(&j->compress, j->payload, j->payload_size); + r = import_uncompress_detect(&j->compress, j->payload.iov_base, j->payload.iov_len); if (r < 0) return log_error_errno(r, "Failed to initialize compressor: %m"); if (r == 0) @@ -451,15 +489,11 @@ static int pull_job_detect_compression(PullJob *j) { return r; /* Now, take the payload we read so far, and decompress it */ - stub = j->payload; - stub_size = j->payload_size; - - j->payload = NULL; - j->payload_size = 0; + _cleanup_(iovec_done) struct iovec stub = TAKE_STRUCT(j->payload); j->state = PULL_JOB_RUNNING; - r = pull_job_write_compressed(j, stub, stub_size); + r = pull_job_write_compressed(j, &stub); if (r < 0) return r; @@ -477,15 +511,11 @@ static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, case PULL_JOB_ANALYZING: /* Let's first check what it actually is */ - - if (!GREEDY_REALLOC(j->payload, j->payload_size + sz)) { + if (!iovec_append(&j->payload, &IOVEC_MAKE(contents, sz))) { r = log_oom(); goto fail; } - memcpy(j->payload + j->payload_size, contents, sz); - j->payload_size += sz; - r = pull_job_detect_compression(j); if (r < 0) goto fail; @@ -493,8 +523,7 @@ static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, break; case PULL_JOB_RUNNING: - - r = pull_job_write_compressed(j, contents, sz); + r = pull_job_write_compressed(j, &IOVEC_MAKE(contents, sz)); if (r < 0) goto fail; @@ -516,16 +545,6 @@ fail: return 0; } -static int http_status_ok(CURLcode status) { - /* Consider all HTTP status code in the 2xx range as OK */ - return status >= 200 && status <= 299; -} - -static int http_status_etag_exists(CURLcode status) { - /* This one is special, it's triggered by our etag mgmt logic */ - return status == 304; -} - static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) { _cleanup_free_ char *length = NULL, *last_modified = NULL, *etag = NULL; size_t sz = size * nmemb; @@ -589,6 +608,12 @@ static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb goto fail; } + if (j->expected_content_length != UINT64_MAX && + j->expected_content_length != j->content_length) { + r = log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Content does not have expected size."); + goto fail; + } + log_info("Downloading %s for %s.", FORMAT_BYTES(j->content_length), j->url); } @@ -691,6 +716,7 @@ int pull_job_new( .url = TAKE_PTR(u), .offset = UINT64_MAX, .sync = true, + .expected_content_length = UINT64_MAX, }; *ret = TAKE_PTR(j); diff --git a/src/import/pull-job.h b/src/import/pull-job.h index b39e11929f..b074238174 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -58,8 +58,9 @@ typedef struct PullJob { uint64_t uncompressed_max; uint64_t compressed_max; - uint8_t *payload; - size_t payload_size; + uint64_t expected_content_length; + + struct iovec payload; int disk_fd; bool close_disk_fd; @@ -76,7 +77,9 @@ typedef struct PullJob { bool calc_checksum; EVP_MD_CTX *checksum_ctx; - char *checksum; + struct iovec checksum; + struct iovec expected_checksum; + bool sync; bool force_memory; } PullJob; diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 6ba8f6cdc9..80ef38f50a 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -67,8 +67,6 @@ typedef struct RawPull { char *verity_path; char *verity_temp_path; - - char *checksum; } RawPull; RawPull* raw_pull_unref(RawPull *i) { @@ -99,7 +97,6 @@ RawPull* raw_pull_unref(RawPull *i) { free(i->verity_path); free(i->image_root); free(i->local); - free(i->checksum); return mfree(i); } @@ -503,13 +500,10 @@ static int raw_pull_rename_auxiliary_file( } static void raw_pull_job_on_finished(PullJob *j) { - RawPull *i; int r; assert(j); - assert(j->userdata); - - i = j->userdata; + RawPull *i = ASSERT_PTR(j->userdata); if (j->error != 0) { /* Only the main job and the checksum job are fatal if they fail. The other fails are just @@ -585,7 +579,6 @@ static void raw_pull_job_on_finished(PullJob *j) { raw_pull_report_progress(i, RAW_VERIFYING); r = pull_verify(i->verify, - i->checksum, i->raw_job, i->checksum_job, i->signature_job, @@ -827,7 +820,7 @@ int raw_pull_start( uint64_t size_max, ImportFlags flags, ImportVerify verify, - const char *checksum) { + const struct iovec *checksum) { int r; @@ -835,11 +828,11 @@ int raw_pull_start( assert(url); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); - assert((verify < 0) || !checksum); + assert((verify < 0) || !iovec_is_set(checksum)); assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_RAW)); assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT)); assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !(flags & IMPORT_DIRECT)); - assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !checksum); + assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !iovec_is_set(checksum)); if (!http_url_is_valid(url) && !file_url_is_valid(url)) return -EINVAL; @@ -854,10 +847,6 @@ int raw_pull_start( if (r < 0) return r; - r = free_and_strdup(&i->checksum, checksum); - if (r < 0) - return r; - i->flags = flags; i->verify = verify; @@ -869,9 +858,12 @@ int raw_pull_start( i->raw_job->on_finished = raw_pull_job_on_finished; i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw; - if (checksum) + if (iovec_is_set(checksum)) { + if (!iovec_memdup(checksum, &i->raw_job->expected_checksum)) + return -ENOMEM; + i->raw_job->calc_checksum = true; - else if (verify != IMPORT_VERIFY_NO) { + } else if (verify != IMPORT_VERIFY_NO) { /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its * signature, which we let gpg verify instead. */ @@ -880,8 +872,8 @@ int raw_pull_start( return r; i->raw_job->calc_checksum = r; - i->raw_job->force_memory = true; /* make sure this is both written to disk if that's - * requested and into memory, since we need to verify it */ + i->raw_job->force_memory = !r; /* make sure this is both written to disk if that's + * requested and into memory, since we need to verify it */ } if (size_max != UINT64_MAX) @@ -899,7 +891,6 @@ int raw_pull_start( &i->checksum_job, &i->signature_job, verify, - i->checksum, url, i->glue, raw_pull_job_on_finished, diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h index c8126d4ff1..0dbd4dcd20 100644 --- a/src/import/pull-raw.h +++ b/src/import/pull-raw.h @@ -14,4 +14,4 @@ RawPull* raw_pull_unref(RawPull *pull); DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref); -int raw_pull_start(RawPull *pull, const char *url, const char *local, uint64_t offset, uint64_t size_max, ImportFlags flags, ImportVerify verify, const char *checksum); +int raw_pull_start(RawPull *pull, const char *url, const char *local, uint64_t offset, uint64_t size_max, ImportFlags flags, ImportVerify verify, const struct iovec *checksum); diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 23bdfb186a..05b58f7976 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -65,8 +65,6 @@ typedef struct TarPull { char *settings_path; char *settings_temp_path; - char *checksum; - int tree_fd; int userns_fd; @@ -98,7 +96,6 @@ TarPull* tar_pull_unref(TarPull *i) { free(i->settings_path); free(i->image_root); free(i->local); - free(i->checksum); safe_close(i->tree_fd); safe_close(i->userns_fd); @@ -402,13 +399,10 @@ static bool tar_pull_is_done(TarPull *i) { } static void tar_pull_job_on_finished(PullJob *j) { - TarPull *i; int r; assert(j); - assert(j->userdata); - - i = j->userdata; + TarPull *i = ASSERT_PTR(j->userdata); if (j->error != 0) { clear_progress_bar(/* prefix= */ NULL); @@ -478,7 +472,6 @@ static void tar_pull_job_on_finished(PullJob *j) { clear_progress_bar(/* prefix= */ NULL); r = pull_verify(i->verify, - i->checksum, i->tar_job, i->checksum_job, i->signature_job, @@ -586,13 +579,11 @@ finish: static int tar_pull_job_on_open_disk_tar(PullJob *j) { const char *where; - TarPull *i; int r; assert(j); - assert(j->userdata); - i = j->userdata; + TarPull *i = ASSERT_PTR(j->userdata); assert(i->tar_job == j); assert(!pidref_is_set(&i->tar_pid)); assert(i->tree_fd < 0); @@ -698,17 +689,17 @@ int tar_pull_start( const char *local, ImportFlags flags, ImportVerify verify, - const char *checksum) { + const struct iovec *checksum) { int r; assert(i); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); - assert((verify < 0) || !checksum); + assert((verify < 0) || !iovec_is_set(checksum)); assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_TAR)); assert(!(flags & IMPORT_PULL_SETTINGS) || !(flags & IMPORT_DIRECT)); - assert(!(flags & IMPORT_PULL_SETTINGS) || !checksum); + assert(!(flags & IMPORT_PULL_SETTINGS) || !iovec_is_set(checksum)); if (!http_url_is_valid(url) && !file_url_is_valid(url)) return -EINVAL; @@ -723,10 +714,6 @@ int tar_pull_start( if (r < 0) return r; - r = free_and_strdup(&i->checksum, checksum); - if (r < 0) - return r; - i->flags = flags; i->verify = verify; @@ -737,7 +724,14 @@ int tar_pull_start( i->tar_job->on_finished = tar_pull_job_on_finished; i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar; - i->tar_job->calc_checksum = checksum || IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE); + + if (iovec_is_set(checksum)) { + if (!iovec_memdup(checksum, &i->tar_job->expected_checksum)) + return -ENOMEM; + + i->tar_job->calc_checksum = true; + } else + i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO; if (!FLAGS_SET(flags, IMPORT_DIRECT)) { r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); @@ -750,7 +744,6 @@ int tar_pull_start( &i->checksum_job, &i->signature_job, verify, - checksum, url, i->glue, tar_pull_job_on_finished, diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h index 8f622dff3a..d2118272ff 100644 --- a/src/import/pull-tar.h +++ b/src/import/pull-tar.h @@ -14,4 +14,4 @@ TarPull* tar_pull_unref(TarPull *pull); DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref); -int tar_pull_start(TarPull *pull, const char *url, const char *local, ImportFlags flags, ImportVerify verify, const char *checksum); +int tar_pull_start(TarPull *pull, const char *url, const char *local, ImportFlags flags, ImportVerify verify, const struct iovec *checksum); diff --git a/src/import/pull.c b/src/import/pull.c index a1b9b940fd..a57c1c039d 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -15,6 +15,7 @@ #include "import-common.h" #include "import-util.h" #include "io-util.h" +#include "iovec-util.h" #include "log.h" #include "main-func.h" #include "parse-argument.h" @@ -32,11 +33,11 @@ static char *arg_image_root = NULL; static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; static ImportFlags arg_import_flags = IMPORT_PULL_SETTINGS | IMPORT_PULL_ROOTHASH | IMPORT_PULL_ROOTHASH_SIGNATURE | IMPORT_PULL_VERITY | IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC; static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX; -static char *arg_checksum = NULL; +static struct iovec arg_checksum = {}; static ImageClass arg_class = IMAGE_MACHINE; static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID; -STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep); +STATIC_DESTRUCTOR_REGISTER(arg_checksum, iovec_done); STATIC_DESTRUCTOR_REGISTER(arg_image_root, freep); static int normalize_local(const char *local, const char *url, char **ret) { @@ -162,7 +163,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) { normalized, arg_import_flags & IMPORT_PULL_FLAGS_MASK_TAR, arg_verify, - arg_checksum); + &arg_checksum); if (r < 0) return log_error_errno(r, "Failed to pull image: %m"); @@ -231,7 +232,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { arg_size_max, arg_import_flags & IMPORT_PULL_FLAGS_MASK_RAW, arg_verify, - arg_checksum); + &arg_checksum); if (r < 0) return log_error_errno(r, "Failed to pull image: %m"); @@ -371,7 +372,6 @@ static int parse_argv(int argc, char *argv[]) { v = import_verify_from_string(optarg); if (v < 0) { _cleanup_free_ void *h = NULL; - char *hh; size_t n; /* If this is not a valid verification mode, maybe it's a literally specified @@ -385,11 +385,10 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "64 hex character SHA256 hash required when specifying explicit checksum, %zu specified", n * 2); - hh = hexmem(h, n); /* bring into canonical (lowercase) form */ - if (!hh) - return log_oom(); + iovec_done(&arg_checksum); + arg_checksum.iov_base = TAKE_PTR(h); + arg_checksum.iov_len = n; - free_and_replace(arg_checksum, hh); arg_import_flags &= ~(IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY); arg_verify = _IMPORT_VERIFY_INVALID; } else @@ -542,7 +541,7 @@ static int parse_argv(int argc, char *argv[]) { if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode."); - if (arg_checksum && (arg_import_flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) != 0) + if (iovec_is_set(&arg_checksum) && (arg_import_flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) != 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Literal checksum verification only supported if no associated files are downloaded."); if (!arg_image_root) {