diff options
author | Lennart Poettering <lennart@poettering.net> | 2021-01-19 20:02:34 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-19 20:02:34 +0100 |
commit | f6c9a7ab9386081200a41e13571ee907f6669f58 (patch) | |
tree | 6083508eb11f320843a6d4c3939faf81306a6439 | |
parent | network: add support to RoutingPolicyRule lookup table name (diff) | |
parent | import: refactor how we do gpg validation (diff) | |
download | systemd-f6c9a7ab9386081200a41e13571ee907f6669f58.tar.xz systemd-f6c9a7ab9386081200a41e13571ee907f6669f58.zip |
Merge pull request #18307 from poettering/import-verity-download
importd: when downloading raw image, also download .roothash.p7s and .verity along with it
-rw-r--r-- | src/basic/fs-util.h | 13 | ||||
-rw-r--r-- | src/basic/rm-rf.h | 14 | ||||
-rw-r--r-- | src/import/curl-util.c | 3 | ||||
-rw-r--r-- | src/import/pull-common.c | 200 | ||||
-rw-r--r-- | src/import/pull-common.h | 27 | ||||
-rw-r--r-- | src/import/pull-job.c | 84 | ||||
-rw-r--r-- | src/import/pull-job.h | 12 | ||||
-rw-r--r-- | src/import/pull-raw.c | 258 | ||||
-rw-r--r-- | src/import/pull-raw.h | 3 | ||||
-rw-r--r-- | src/import/pull-tar.c | 98 | ||||
-rw-r--r-- | src/import/pull-tar.h | 3 | ||||
-rw-r--r-- | src/import/pull.c | 58 |
12 files changed, 524 insertions, 249 deletions
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 13b6872942..8bd8da1704 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -100,16 +100,25 @@ int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chas int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd); /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ -static inline void rmdir_and_free(char *p) { +static inline char *rmdir_and_free(char *p) { PROTECT_ERRNO; + + if (!p) + return NULL; + (void) rmdir(p); free(p); + return NULL; } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free); -static inline void unlink_and_free(char *p) { +static inline char* unlink_and_free(char *p) { + if (!p) + return NULL; + (void) unlink_noerrno(p); free(p); + return NULL; } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h index ec56232b5d..6483b30d7e 100644 --- a/src/basic/rm-rf.h +++ b/src/basic/rm-rf.h @@ -18,17 +18,27 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); int rm_rf(const char *path, RemoveFlags flags); /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ -static inline void rm_rf_physical_and_free(char *p) { +static inline char *rm_rf_physical_and_free(char *p) { PROTECT_ERRNO; + + if (!p) + return NULL; + (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); free(p); + return NULL; } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free); /* Similar as above, but also has magic btrfs subvolume powers */ -static inline void rm_rf_subvolume_and_free(char *p) { +static inline char *rm_rf_subvolume_and_free(char *p) { PROTECT_ERRNO; + + if (!p) + return NULL; + (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); free(p); + return NULL; } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_subvolume_and_free); diff --git a/src/import/curl-util.c b/src/import/curl-util.c index 5e0904379e..e6db810635 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -231,7 +231,8 @@ int curl_glue_make(CURL **ret, const char *url, void *userdata) { if (!c) return -ENOMEM; - /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */ + if (DEBUG_LOGGING) + (void) curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) return -EIO; diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 33be609aec..403a0952bc 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -110,7 +110,7 @@ int pull_find_old_etags( return 0; } -int pull_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) { +int pull_make_local_copy(const char *final, const char *image_root, const char *local, PullFlags flags) { const char *p; int r; @@ -122,7 +122,7 @@ int pull_make_local_copy(const char *final, const char *image_root, const char * p = prefix_roota(image_root, local); - if (force_local) + if (FLAGS_SET(flags, PULL_FORCE)) (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); r = btrfs_subvol_snapshot(final, p, @@ -255,7 +255,6 @@ 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); @@ -266,6 +265,7 @@ int pull_make_verification_jobs( if (verify != IMPORT_VERIFY_NO) { _cleanup_free_ char *checksum_url = NULL, *fn = NULL; + const char *chksums = NULL; /* Queue jobs for the checksum file for the image. */ r = import_url_last_component(url, &fn); @@ -302,10 +302,8 @@ int pull_make_verification_jobs( signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL; } - *ret_checksum_job = checksum_job; - *ret_signature_job = signature_job; - - checksum_job = signature_job = NULL; + *ret_checksum_job = TAKE_PTR(checksum_job); + *ret_signature_job = TAKE_PTR(signature_job); return 0; } @@ -365,70 +363,35 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { return 1; } -int pull_verify(PullJob *main_job, - PullJob *roothash_job, - PullJob *settings_job, - PullJob *checksum_job, - PullJob *signature_job) { +static int verify_gpg( + const void *payload, size_t payload_size, + const void *signature, size_t signature_size) { _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 }; - _cleanup_close_ int sig_file = -1; char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; _cleanup_(sigkill_waitp) pid_t pid = 0; bool gpg_home_created = false; int r; - assert(main_job); - assert(main_job->state == PULL_JOB_DONE); - - if (!checksum_job) - return 0; - - assert(main_job->calc_checksum); - assert(main_job->checksum); - - assert(checksum_job->state == PULL_JOB_DONE); - - if (!checksum_job->payload || checksum_job->payload_size <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), - "Checksum is empty, cannot verify."); - - r = verify_one(checksum_job, main_job); - if (r < 0) - return r; - - r = verify_one(checksum_job, roothash_job); - if (r < 0) - return r; - - r = verify_one(checksum_job, settings_job); - if (r < 0) - return r; - - 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) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), - "Signature is empty, cannot verify."); + assert(payload || payload_size == 0); + assert(signature || signature_size == 0); r = pipe2(gpg_pipe, O_CLOEXEC); if (r < 0) return log_error_errno(errno, "Failed to create pipe for gpg: %m"); - sig_file = mkostemp(sig_file_path, O_RDWR); - if (sig_file < 0) - return log_error_errno(errno, "Failed to create temporary file: %m"); + if (signature_size > 0) { + _cleanup_close_ int sig_file = -1; - r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false); - if (r < 0) { - log_error_errno(r, "Failed to write to temporary file: %m"); - goto finish; + 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, false); + if (r < 0) { + log_error_errno(r, "Failed to write to temporary file: %m"); + goto finish; + } } if (!mkdtemp(gpg_home)) { @@ -457,7 +420,7 @@ int pull_verify(PullJob *main_job, NULL, /* dash */ NULL /* trailing NULL */ }; - unsigned k = ELEMENTSOF(cmd) - 6; + size_t k = ELEMENTSOF(cmd) - 6; /* Child */ @@ -473,8 +436,7 @@ int pull_verify(PullJob *main_job, cmd[k++] = strjoina("--homedir=", gpg_home); - /* We add the user keyring only to the command line - * arguments, if it's around since gpg fails + /* We add the user keyring only to the command line arguments, if it's around since gpg fails * otherwise. */ if (access(USER_KEYRING_PATH, F_OK) >= 0) cmd[k++] = "--keyring=" USER_KEYRING_PATH; @@ -482,7 +444,7 @@ int pull_verify(PullJob *main_job, cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH; cmd[k++] = "--verify"; - if (checksum_job->style == VERIFICATION_PER_DIRECTORY) { + if (signature) { cmd[k++] = sig_file_path; cmd[k++] = "-"; cmd[k++] = NULL; @@ -496,7 +458,7 @@ int pull_verify(PullJob *main_job, gpg_pipe[0] = safe_close(gpg_pipe[0]); - r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false); + r = loop_write(gpg_pipe[1], payload, payload_size, false); if (r < 0) { log_error_errno(r, "Failed to write to pipe: %m"); goto finish; @@ -517,10 +479,120 @@ int pull_verify(PullJob *main_job, } finish: - (void) unlink(sig_file_path); + if (signature_size > 0) + (void) unlink(sig_file_path); if (gpg_home_created) (void) rm_rf(gpg_home, REMOVE_ROOT|REMOVE_PHYSICAL); return r; } + +int pull_verify(ImportVerify verify, + PullJob *main_job, + PullJob *roothash_job, + PullJob *settings_job, + PullJob *checksum_job, + PullJob *signature_job) { + + VerificationStyle style; + int r; + + assert(main_job); + assert(main_job->state == PULL_JOB_DONE); + + if (verify == IMPORT_VERIFY_NO) + return 0; + + assert(main_job->calc_checksum); + assert(main_job->checksum); + assert(checksum_job); + assert(checksum_job->state == PULL_JOB_DONE); + + if (!checksum_job->payload || checksum_job->payload_size <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "Checksum is empty, cannot verify."); + + r = verify_one(checksum_job, main_job); + if (r < 0) + return r; + + r = verify_one(checksum_job, roothash_job); + if (r < 0) + return r; + + r = verify_one(checksum_job, settings_job); + if (r < 0) + return r; + + if (verify == IMPORT_VERIFY_CHECKSUM) + return 0; + + 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_DIRECTORY) { + assert(signature_job); + assert(signature_job->state == PULL_JOB_DONE); + + if (!signature_job->payload || signature_job->payload_size <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "Signature is empty, cannot verify."); + + return verify_gpg(checksum_job->payload, checksum_job->payload_size, signature_job->payload, signature_job->payload_size); + } else + return verify_gpg(checksum_job->payload, checksum_job->payload_size, NULL, 0); +} + +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..acbbab7eab 100644 --- a/src/import/pull-common.h +++ b/src/import/pull-common.h @@ -6,7 +6,19 @@ #include "import-util.h" #include "pull-job.h" -int pull_make_local_copy(const char *final, const char *root, const char *local, bool force_local); +typedef enum PullFlags { + PULL_FORCE = 1 << 0, /* replace existing image */ + PULL_SETTINGS = 1 << 1, /* .nspawn settings file */ + PULL_ROOTHASH = 1 << 2, /* only for raw: .roothash file for verity */ + PULL_ROOTHASH_SIGNATURE = 1 << 3, /* only for raw: .roothash.p7s file for verity */ + PULL_VERITY = 1 << 4, /* only for raw: .verity file for verity */ + + /* The supported flags for the tar and the raw pulling */ + PULL_FLAGS_MASK_TAR = PULL_FORCE|PULL_SETTINGS, + PULL_FLAGS_MASK_RAW = PULL_FORCE|PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY, +} PullFlags; + +int pull_make_local_copy(const char *final, const char *root, const char *local, PullFlags flags); int pull_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags); @@ -15,4 +27,15 @@ int pull_make_path(const char *url, const char *etag, const char *image_root, co int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, 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(PullJob *main_job, PullJob *roothash_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job); +int pull_verify(ImportVerify 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..d1e61ba601 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -61,22 +61,41 @@ 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->error = 0; j->payload = mfree(j->payload); j->payload_size = 0; j->payload_allocated = 0; 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); + + curl_glue_remove_and_free(j->glue, j->curl); + j->curl = NULL; + + curl_slist_free_all(j->request_header); + j->request_header = NULL; + + import_compress_free(&j->compress); + + if (j->checksum_context) { + gcry_md_close(j->checksum_context); + j->checksum_context = NULL; + } r = pull_job_begin(j); if (r < 0) @@ -114,23 +133,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; - if (status == 0) { - j->style = VERIFICATION_PER_DIRECTORY; - return; + 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; } } @@ -407,10 +434,11 @@ fail: } static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) { - PullJob *j = userdata; + _cleanup_free_ char *length = NULL, *last_modified = NULL, *etag = NULL; size_t sz = size * nmemb; - _cleanup_free_ char *length = NULL, *last_modified = NULL; - char *etag; + PullJob *j = userdata; + CURLcode code; + long status; int r; assert(contents); @@ -423,14 +451,25 @@ static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb assert(j->state == PULL_JOB_ANALYZING); + 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 fail; + } + + if (status < 200 || status >= 300) + /* If this is not HTTP 2xx, let's skip these headers, they are probably for + * some redirect or so, and we are not interested in the headers of those. */ + return sz; + r = curl_header_strdup(contents, sz, "ETag:", &etag); if (r < 0) { log_oom(); goto fail; } if (r > 0) { - free(j->etag); - j->etag = etag; + free_and_replace(j->etag, etag); if (strv_contains(j->old_etags, j->etag)) { log_info("Image already downloaded. Skipping download."); @@ -556,7 +595,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 719196caec..48cc773beb 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -13,23 +13,18 @@ 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, PULL_JOB_ANALYZING, /* Still reading into ->payload, to figure out what we have */ - PULL_JOB_RUNNING, /* Writing to destination */ + PULL_JOB_RUNNING, /* Writing to destination */ PULL_JOB_DONE, PULL_JOB_FAILED, _PULL_JOB_STATE_MAX, _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..0985dddef3 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -42,21 +42,22 @@ struct RawPull { sd_event *event; CurlGlue *glue; + PullFlags flags; + ImportVerify verify; char *image_root; PullJob *raw_job; - PullJob *roothash_job; - PullJob *settings_job; PullJob *checksum_job; PullJob *signature_job; + PullJob *settings_job; + PullJob *roothash_job; + PullJob *roothash_signature_job; + PullJob *verity_job; RawPullFinished on_finished; void *userdata; char *local; - bool force_local; - bool settings; - bool roothash; char *final_path; char *temp_path; @@ -67,7 +68,11 @@ struct RawPull { char *roothash_path; char *roothash_temp_path; - ImportVerify verify; + char *roothash_signature_path; + char *roothash_signature_temp_path; + + char *verity_path; + char *verity_temp_path; }; RawPull* raw_pull_unref(RawPull *i) { @@ -75,34 +80,30 @@ RawPull* raw_pull_unref(RawPull *i) { return NULL; pull_job_unref(i->raw_job); - pull_job_unref(i->settings_job); - pull_job_unref(i->roothash_job); pull_job_unref(i->checksum_job); pull_job_unref(i->signature_job); + pull_job_unref(i->settings_job); + pull_job_unref(i->roothash_job); + pull_job_unref(i->roothash_signature_job); + pull_job_unref(i->verity_job); curl_glue_unref(i->glue); sd_event_unref(i->event); - if (i->temp_path) { - (void) unlink(i->temp_path); - free(i->temp_path); - } - - if (i->roothash_temp_path) { - (void) unlink(i->roothash_temp_path); - free(i->roothash_temp_path); - } - - if (i->settings_temp_path) { - (void) unlink(i->settings_temp_path); - free(i->settings_temp_path); - } + unlink_and_free(i->temp_path); + unlink_and_free(i->settings_temp_path); + unlink_and_free(i->roothash_temp_path); + unlink_and_free(i->roothash_signature_temp_path); + unlink_and_free(i->verity_temp_path); free(i->final_path); - free(i->roothash_path); free(i->settings_path); + free(i->roothash_path); + free(i->roothash_signature_path); + free(i->verity_path); free(i->image_root); free(i->local); + return mfree(i); } @@ -169,6 +170,16 @@ static void raw_pull_report_progress(RawPull *i, RawProgress p) { percent = 0; + if (i->checksum_job) { + percent += i->checksum_job->progress_percent * 5 / 100; + remain -= 5; + } + + if (i->signature_job) { + percent += i->signature_job->progress_percent * 5 / 100; + remain -= 5; + } + if (i->settings_job) { percent += i->settings_job->progress_percent * 5 / 100; remain -= 5; @@ -179,14 +190,14 @@ static void raw_pull_report_progress(RawPull *i, RawProgress p) { remain -= 5; } - if (i->checksum_job) { - percent += i->checksum_job->progress_percent * 5 / 100; + if (i->roothash_signature_job) { + percent += i->roothash_signature_job->progress_percent * 5 / 100; remain -= 5; } - if (i->signature_job) { - percent += i->signature_job->progress_percent * 5 / 100; - remain -= 5; + if (i->verity_job) { + percent += i->verity_job->progress_percent * 10 / 100; + remain -= 10; } if (i->raw_job) @@ -294,7 +305,7 @@ static int raw_pull_copy_auxiliary_file( local = strjoina(i->image_root, "/", i->local, suffix); - r = copy_file_atomic(*path, local, 0644, 0, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0)); + r = copy_file_atomic(*path, local, 0644, 0, 0, COPY_REFLINK | (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0)); if (r == -EEXIST) log_warning_errno(r, "File %s already exists, not replacing.", local); else if (r == -ENOENT) @@ -338,7 +349,7 @@ static int raw_pull_make_local_copy(RawPull *i) { p = strjoina(i->image_root, "/", i->local, ".raw"); - if (i->force_local) + if (FLAGS_SET(i->flags, PULL_FORCE)) (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); r = tempfn_random(p, NULL, &tp); @@ -373,14 +384,26 @@ static int raw_pull_make_local_copy(RawPull *i) { log_info("Created new local image '%s'.", i->local); - if (i->roothash) { + if (FLAGS_SET(i->flags, PULL_SETTINGS)) { + r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path); + if (r < 0) + return r; + } + + if (FLAGS_SET(i->flags, PULL_ROOTHASH)) { r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path); if (r < 0) return r; } - if (i->settings) { - r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path); + if (FLAGS_SET(i->flags, PULL_ROOTHASH_SIGNATURE)) { + r = raw_pull_copy_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_path); + if (r < 0) + return r; + } + + if (FLAGS_SET(i->flags, PULL_VERITY)) { + r = raw_pull_copy_auxiliary_file(i, ".verity", &i->verity_path); if (r < 0) return r; } @@ -394,13 +417,17 @@ static bool raw_pull_is_done(RawPull *i) { if (!PULL_JOB_IS_COMPLETE(i->raw_job)) return false; - if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job)) + if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) + return false; + if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) return false; if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) return false; - if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) + if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job)) return false; - if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) + if (i->roothash_signature_job && !PULL_JOB_IS_COMPLETE(i->roothash_signature_job)) + return false; + if (i->verity_job && !PULL_JOB_IS_COMPLETE(i->verity_job)) return false; return true; @@ -447,12 +474,18 @@ static void raw_pull_job_on_finished(PullJob *j) { assert(j->userdata); i = j->userdata; - if (j == i->roothash_job) { + 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 == i->roothash_job) { if (j->error != 0) log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without."); - } else if (j == i->settings_job) { + } else if (j == i->roothash_signature_job) { if (j->error != 0) - log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); + log_info_errno(j->error, "Root hash signature file could not be retrieved, proceeding without."); + } else if (j == i->verity_job) { + if (j->error != 0) + log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without. %s", j->url); } 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?)"); @@ -463,27 +496,41 @@ static void raw_pull_job_on_finished(PullJob *j) { goto finish; } - /* This is invoked if either the download completed - * successfully, or the download was skipped because we - * already have the etag. In this case ->etag_exists is - * true. + /* This is invoked if either the download completed successfully, or the download was skipped because + * we already have the etag. In this case ->etag_exists is true. * * We only do something when we got all three files */ 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) - i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd); if (i->settings_job) i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); + if (i->roothash_job) + i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd); + if (i->roothash_signature_job) + i->roothash_signature_job->disk_fd = safe_close(i->roothash_signature_job->disk_fd); + if (i->verity_job) + i->verity_job->disk_fd = safe_close(i->verity_job->disk_fd); r = raw_pull_determine_path(i, ".raw", &i->final_path); if (r < 0) @@ -495,7 +542,7 @@ static void raw_pull_job_on_finished(PullJob *j) { raw_pull_report_progress(i, RAW_VERIFYING); - r = pull_verify(i->raw_job, i->roothash_job, i->settings_job, i->checksum_job, i->signature_job); + r = pull_verify(i->verify, i->raw_job, i->roothash_job, i->settings_job, i->checksum_job, i->signature_job); if (r < 0) goto finish; @@ -598,6 +645,18 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) { return 0; } +static int raw_pull_job_on_open_disk_settings(PullJob *j) { + RawPull *i; + + assert(j); + assert(j->userdata); + + i = j->userdata; + assert(i->settings_job == j); + + return raw_pull_job_on_open_disk_generic(i, j, "settings", &i->settings_temp_path); +} + static int raw_pull_job_on_open_disk_roothash(PullJob *j) { RawPull *i; @@ -610,16 +669,28 @@ static int raw_pull_job_on_open_disk_roothash(PullJob *j) { return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path); } -static int raw_pull_job_on_open_disk_settings(PullJob *j) { +static int raw_pull_job_on_open_disk_roothash_signature(PullJob *j) { RawPull *i; assert(j); assert(j->userdata); i = j->userdata; - assert(i->settings_job == j); + assert(i->roothash_signature_job == j); - return raw_pull_job_on_open_disk_generic(i, j, "settings", &i->settings_temp_path); + return raw_pull_job_on_open_disk_generic(i, j, "roothash.p7s", &i->roothash_signature_temp_path); +} + +static int raw_pull_job_on_open_disk_verity(PullJob *j) { + RawPull *i; + + assert(j); + assert(j->userdata); + + i = j->userdata; + assert(i->verity_job == j); + + return raw_pull_job_on_open_disk_generic(i, j, "verity", &i->verity_temp_path); } static void raw_pull_job_on_progress(PullJob *j) { @@ -637,16 +708,15 @@ int raw_pull_start( RawPull *i, const char *url, const char *local, - bool force_local, - ImportVerify verify, - bool settings, - bool roothash) { + PullFlags flags, + ImportVerify verify) { int r; assert(i); assert(verify < _IMPORT_VERIFY_MAX); assert(verify >= 0); + assert(!(flags & ~PULL_FLAGS_MASK_RAW)); if (!http_url_is_valid(url)) return -EINVAL; @@ -661,10 +731,8 @@ int raw_pull_start( if (r < 0) return r; - i->force_local = force_local; + i->flags = flags; i->verify = verify; - i->settings = settings; - i->roothash = roothash; /* Queue job for the image itself */ r = pull_job_new(&i->raw_job, url, i->glue, i); @@ -680,7 +748,21 @@ int raw_pull_start( if (r < 0) return r; - if (roothash) { + r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i); + if (r < 0) + return r; + + if (FLAGS_SET(flags, PULL_SETTINGS)) { + r = pull_make_auxiliary_job(&i->settings_job, url, raw_strip_suffixes, ".nspawn", i->glue, raw_pull_job_on_finished, i); + if (r < 0) + return r; + + i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings; + i->settings_job->on_progress = raw_pull_job_on_progress; + i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; + } + + if (FLAGS_SET(flags, PULL_ROOTHASH)) { r = pull_make_auxiliary_job(&i->roothash_job, url, raw_strip_suffixes, ".roothash", i->glue, raw_pull_job_on_finished, i); if (r < 0) return r; @@ -690,26 +772,43 @@ int raw_pull_start( i->roothash_job->calc_checksum = verify != IMPORT_VERIFY_NO; } - if (settings) { - r = pull_make_auxiliary_job(&i->settings_job, url, raw_strip_suffixes, ".nspawn", i->glue, raw_pull_job_on_finished, i); + if (FLAGS_SET(flags, PULL_ROOTHASH_SIGNATURE)) { + r = pull_make_auxiliary_job(&i->roothash_signature_job, url, raw_strip_suffixes, ".roothash.p7s", i->glue, raw_pull_job_on_finished, i); if (r < 0) return r; - i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings; - i->settings_job->on_progress = raw_pull_job_on_progress; - i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; + i->roothash_signature_job->on_open_disk = raw_pull_job_on_open_disk_roothash_signature; + i->roothash_signature_job->on_progress = raw_pull_job_on_progress; + i->roothash_signature_job->calc_checksum = verify != IMPORT_VERIFY_NO; } - r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i); - if (r < 0) - return r; + if (FLAGS_SET(flags, PULL_VERITY)) { + r = pull_make_auxiliary_job(&i->verity_job, url, raw_strip_suffixes, ".verity", i->glue, raw_pull_job_on_finished, i); + if (r < 0) + return r; + + i->verity_job->on_open_disk = raw_pull_job_on_open_disk_verity; + i->verity_job->on_progress = raw_pull_job_on_progress; + i->verity_job->calc_checksum = verify != IMPORT_VERIFY_NO; + } r = pull_job_begin(i->raw_job); if (r < 0) return r; - if (i->roothash_job) { - r = pull_job_begin(i->roothash_job); + if (i->checksum_job) { + i->checksum_job->on_progress = raw_pull_job_on_progress; + i->checksum_job->on_not_found = pull_job_restart_with_sha256sum; + + r = pull_job_begin(i->checksum_job); + if (r < 0) + return r; + } + + if (i->signature_job) { + i->signature_job->on_progress = raw_pull_job_on_progress; + + r = pull_job_begin(i->signature_job); if (r < 0) return r; } @@ -720,19 +819,20 @@ int raw_pull_start( return r; } - 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 (i->roothash_job) { + r = pull_job_begin(i->roothash_job); if (r < 0) return r; } - if (i->signature_job) { - i->signature_job->on_progress = raw_pull_job_on_progress; + if (i->roothash_signature_job) { + r = pull_job_begin(i->roothash_signature_job); + if (r < 0) + return r; + } - r = pull_job_begin(i->signature_job); + if (i->verity_job) { + r = pull_job_begin(i->verity_job); if (r < 0) return r; } diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h index e1d450d9df..985bda4762 100644 --- a/src/import/pull-raw.h +++ b/src/import/pull-raw.h @@ -5,6 +5,7 @@ #include "import-util.h" #include "macro.h" +#include "pull-common.h" typedef struct RawPull RawPull; @@ -15,4 +16,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, bool force_local, ImportVerify verify, bool settings, bool roothash); +int raw_pull_start(RawPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify); diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index b646d38539..b52569d20c 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -40,19 +40,19 @@ struct TarPull { sd_event *event; CurlGlue *glue; + PullFlags flags; + ImportVerify verify; char *image_root; PullJob *tar_job; - PullJob *settings_job; PullJob *checksum_job; PullJob *signature_job; + PullJob *settings_job; TarPullFinished on_finished; void *userdata; char *local; - bool force_local; - bool settings; pid_t tar_pid; @@ -61,8 +61,6 @@ struct TarPull { char *settings_path; char *settings_temp_path; - - ImportVerify verify; }; TarPull* tar_pull_unref(TarPull *i) { @@ -75,22 +73,15 @@ TarPull* tar_pull_unref(TarPull *i) { } pull_job_unref(i->tar_job); - pull_job_unref(i->settings_job); pull_job_unref(i->checksum_job); pull_job_unref(i->signature_job); + pull_job_unref(i->settings_job); curl_glue_unref(i->glue); sd_event_unref(i->event); - if (i->temp_path) { - (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - free(i->temp_path); - } - - if (i->settings_temp_path) { - (void) unlink(i->settings_temp_path); - free(i->settings_temp_path); - } + rm_rf_subvolume_and_free(i->temp_path); + unlink_and_free(i->settings_temp_path); free(i->final_path); free(i->settings_path); @@ -163,11 +154,6 @@ static void tar_pull_report_progress(TarPull *i, TarProgress p) { percent = 0; - if (i->settings_job) { - percent += i->settings_job->progress_percent * 5 / 100; - remain -= 5; - } - if (i->checksum_job) { percent += i->checksum_job->progress_percent * 5 / 100; remain -= 5; @@ -178,6 +164,11 @@ static void tar_pull_report_progress(TarPull *i, TarProgress p) { remain -= 5; } + if (i->settings_job) { + percent += i->settings_job->progress_percent * 5 / 100; + remain -= 5; + } + if (i->tar_job) percent += i->tar_job->progress_percent * remain / 100; break; @@ -230,11 +221,11 @@ static int tar_pull_make_local_copy(TarPull *i) { if (!i->local) return 0; - r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local); + r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->flags); if (r < 0) return r; - if (i->settings) { + if (FLAGS_SET(i->flags, PULL_SETTINGS)) { const char *local_settings; assert(i->settings_job); @@ -244,7 +235,7 @@ static int tar_pull_make_local_copy(TarPull *i) { local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); - r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0)); + r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, 0, COPY_REFLINK | (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0)); if (r == -EEXIST) log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); else if (r == -ENOENT) @@ -264,12 +255,12 @@ static bool tar_pull_is_done(TarPull *i) { if (!PULL_JOB_IS_COMPLETE(i->tar_job)) return false; - if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) - return false; if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) return false; if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) return false; + if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) + return false; return true; } @@ -302,11 +293,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); @@ -333,7 +336,7 @@ static void tar_pull_job_on_finished(PullJob *j) { tar_pull_report_progress(i, TAR_VERIFYING); - r = pull_verify(i->tar_job, NULL, i->settings_job, i->checksum_job, i->signature_job); + r = pull_verify(i->verify, i->tar_job, NULL, i->settings_job, i->checksum_job, i->signature_job); if (r < 0) goto finish; @@ -471,15 +474,15 @@ int tar_pull_start( TarPull *i, const char *url, const char *local, - bool force_local, - ImportVerify verify, - bool settings) { + PullFlags flags, + ImportVerify verify) { int r; assert(i); assert(verify < _IMPORT_VERIFY_MAX); assert(verify >= 0); + assert(!(flags & ~PULL_FLAGS_MASK_TAR)); if (!http_url_is_valid(url)) return -EINVAL; @@ -494,9 +497,8 @@ int tar_pull_start( if (r < 0) return r; - i->force_local = force_local; + i->flags = flags; i->verify = verify; - i->settings = settings; /* Set up download job for TAR file */ r = pull_job_new(&i->tar_job, url, i->glue, i); @@ -512,8 +514,13 @@ int tar_pull_start( if (r < 0) return r; + /* Set up download of checksum/signature files */ + r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i); + if (r < 0) + return r; + /* Set up download job for the settings file (.nspawn) */ - if (settings) { + if (FLAGS_SET(flags, PULL_SETTINGS)) { r = pull_make_auxiliary_job(&i->settings_job, url, tar_strip_suffixes, ".nspawn", i->glue, tar_pull_job_on_finished, i); if (r < 0) return r; @@ -523,24 +530,13 @@ int tar_pull_start( i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; } - /* Set up download of checksum/signature files */ - r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i); - if (r < 0) - return r; - r = pull_job_begin(i->tar_job); if (r < 0) return r; - if (i->settings_job) { - r = pull_job_begin(i->settings_job); - if (r < 0) - return r; - } - 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) @@ -555,5 +551,11 @@ int tar_pull_start( return r; } + if (i->settings_job) { + r = pull_job_begin(i->settings_job); + if (r < 0) + return r; + } + return 0; } diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h index 78d982cf5a..414077549f 100644 --- a/src/import/pull-tar.h +++ b/src/import/pull-tar.h @@ -5,6 +5,7 @@ #include "import-util.h" #include "macro.h" +#include "pull-common.h" typedef struct TarPull TarPull; @@ -15,4 +16,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, bool force_local, ImportVerify verify, bool settings); +int tar_pull_start(TarPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify); diff --git a/src/import/pull.c b/src/import/pull.c index e80d8abe6f..33e6196f79 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -19,11 +19,9 @@ #include "verbs.h" #include "web-util.h" -static bool arg_force = false; static const char *arg_image_root = "/var/lib/machines"; static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; -static bool arg_settings = true; -static bool arg_roothash = true; +static PullFlags arg_pull_flags = PULL_SETTINGS | PULL_ROOTHASH | PULL_ROOTHASH_SIGNATURE | PULL_VERITY; static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { log_notice("Transfer aborted."); @@ -77,7 +75,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) { "Local image name '%s' is not valid.", local); - if (!arg_force) { + if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) { r = image_find(IMAGE_MACHINE, local, NULL, NULL); if (r < 0) { if (r != -ENOENT) @@ -105,7 +103,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to allocate puller: %m"); - r = tar_pull_start(pull, url, local, arg_force, arg_verify, arg_settings); + r = tar_pull_start(pull, url, local, arg_pull_flags & PULL_FLAGS_MASK_TAR, arg_verify); if (r < 0) return log_error_errno(r, "Failed to pull image: %m"); @@ -163,7 +161,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { "Local image name '%s' is not valid.", local); - if (!arg_force) { + if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) { r = image_find(IMAGE_MACHINE, local, NULL, NULL); if (r < 0) { if (r != -ENOENT) @@ -191,7 +189,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to allocate puller: %m"); - r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings, arg_roothash); + r = raw_pull_start(pull, url, local, arg_pull_flags & PULL_FLAGS_MASK_RAW, arg_verify); if (r < 0) return log_error_errno(r, "Failed to pull image: %m"); @@ -214,6 +212,8 @@ static int help(int argc, char *argv[], void *userdata) { " 'checksum', 'signature'\n" " --settings=BOOL Download settings file with image\n" " --roothash=BOOL Download root hash file with image\n" + " --roothash-sigature=BOOL Download root hash signature file with image\n" + " --verity=BOOL Download verity file with image\n" " --image-root=PATH Image root directory\n\n" "Commands:\n" " tar URL [NAME] Download a TAR image\n" @@ -232,16 +232,20 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERIFY, ARG_SETTINGS, ARG_ROOTHASH, + ARG_ROOTHASH_SIGNATURE, + ARG_VERITY, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "force", no_argument, NULL, ARG_FORCE }, - { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, - { "verify", required_argument, NULL, ARG_VERIFY }, - { "settings", required_argument, NULL, ARG_SETTINGS }, - { "roothash", required_argument, NULL, ARG_ROOTHASH }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "force", no_argument, NULL, ARG_FORCE }, + { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, + { "verify", required_argument, NULL, ARG_VERIFY }, + { "settings", required_argument, NULL, ARG_SETTINGS }, + { "roothash", required_argument, NULL, ARG_ROOTHASH }, + { "roothash-signature", required_argument, NULL, ARG_ROOTHASH_SIGNATURE }, + { "verity", required_argument, NULL, ARG_VERITY }, {} }; @@ -261,7 +265,7 @@ static int parse_argv(int argc, char *argv[]) { return version(); case ARG_FORCE: - arg_force = true; + arg_pull_flags |= PULL_FORCE; break; case ARG_IMAGE_ROOT: @@ -281,7 +285,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse --settings= parameter '%s': %m", optarg); - arg_settings = r; + SET_FLAG(arg_pull_flags, PULL_SETTINGS, r); break; case ARG_ROOTHASH: @@ -289,7 +293,27 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse --roothash= parameter '%s': %m", optarg); - arg_roothash = r; + SET_FLAG(arg_pull_flags, PULL_ROOTHASH, r); + + /* If we were asked to turn off the root hash, implicitly also turn off the root hash signature */ + if (!r) + SET_FLAG(arg_pull_flags, PULL_ROOTHASH_SIGNATURE, false); + break; + + case ARG_ROOTHASH_SIGNATURE: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --roothash-signature= parameter '%s': %m", optarg); + + SET_FLAG(arg_pull_flags, PULL_ROOTHASH_SIGNATURE, r); + break; + + case ARG_VERITY: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --verity= parameter '%s': %m", optarg); + + SET_FLAG(arg_pull_flags, PULL_VERITY, r); break; case '?': |