1
0
mirror of https://github.com/systemd/systemd synced 2025-11-10 20:34:45 +01:00

Compare commits

..

No commits in common. "aae054e1ab156d44f669c60140c213c0ec35b2d0" and "f8293452b65551c75534a35af8e6202a45837299" have entirely different histories.

10 changed files with 168 additions and 179 deletions

View File

@ -6,7 +6,6 @@
#include "dirent-util.h" #include "dirent-util.h"
#include "escape.h" #include "escape.h"
#include "fd-util.h" #include "fd-util.h"
#include "hexdecoct.h"
#include "io-util.h" #include "io-util.h"
#include "log.h" #include "log.h"
#include "memory-util.h" #include "memory-util.h"
@ -248,6 +247,7 @@ int pull_make_verification_jobs(
PullJob **ret_checksum_job, PullJob **ret_checksum_job,
PullJob **ret_signature_job, PullJob **ret_signature_job,
ImportVerify verify, 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, const char *url,
CurlGlue *glue, CurlGlue *glue,
PullJobFinished on_finished, PullJobFinished on_finished,
@ -261,13 +261,13 @@ int pull_make_verification_jobs(
assert(ret_signature_job); assert(ret_signature_job);
assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
assert((verify < 0) || !checksum);
assert(url); assert(url);
assert(glue); assert(glue);
/* If verification is turned off, or if the checksum to validate is already specified we don't need /* 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 */ * to download a checksum file or signature, hence shortcut things */
if (verify < 0 || /* verification already done (via literal checksum) */ if (verify == IMPORT_VERIFY_NO || checksum) {
verify == IMPORT_VERIFY_NO) { /* verification turned off */
*ret_checksum_job = *ret_signature_job = NULL; *ret_checksum_job = *ret_signature_job = NULL;
return 0; return 0;
} }
@ -351,7 +351,7 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
return 0; return 0;
assert(job->calc_checksum); assert(job->calc_checksum);
assert(iovec_is_set(&job->checksum)); assert(job->checksum);
r = import_url_last_component(job->url, &fn); r = import_url_last_component(job->url, &fn);
if (r < 0) if (r < 0)
@ -365,10 +365,6 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
return log_error_errno(SYNTHETIC_ERRNO(ELOOP), return log_error_errno(SYNTHETIC_ERRNO(ELOOP),
"Cannot verify checksum/signature files via themselves."); "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; const char *p = NULL;
FOREACH_STRING(separator, FOREACH_STRING(separator,
" *", /* separator for binary mode */ " *", /* separator for binary mode */
@ -376,12 +372,12 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
" " /* non-standard separator used by linuxcontainers.org */) { " " /* non-standard separator used by linuxcontainers.org */) {
_cleanup_free_ char *line = NULL; _cleanup_free_ char *line = NULL;
line = strjoin(cs, separator, fn, "\n"); line = strjoin(job->checksum, separator, fn, "\n");
if (!line) if (!line)
return log_oom(); return log_oom();
p = memmem_safe(checksum_job->payload.iov_base, p = memmem_safe(checksum_job->payload,
checksum_job->payload.iov_len, checksum_job->payload_size,
line, line,
strlen(line)); strlen(line));
if (p) if (p)
@ -389,7 +385,7 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
} }
/* Only counts if found at beginning of a line */ /* Only counts if found at beginning of a line */
if (!p || (p != (char*) checksum_job->payload.iov_base && p[-1] != '\n')) if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n'))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
"DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", fn); "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", fn);
@ -398,8 +394,8 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
} }
static int verify_gpg( static int verify_gpg(
const struct iovec *payload, const void *payload, size_t payload_size,
const struct iovec *signature) { const void *signature, size_t signature_size) {
_cleanup_close_pair_ int gpg_pipe[2] = EBADF_PAIR; _cleanup_close_pair_ int gpg_pipe[2] = EBADF_PAIR;
_cleanup_(rm_rf_physical_and_freep) char *gpg_home = NULL; _cleanup_(rm_rf_physical_and_freep) char *gpg_home = NULL;
@ -407,21 +403,21 @@ static int verify_gpg(
_cleanup_(sigkill_waitp) pid_t pid = 0; _cleanup_(sigkill_waitp) pid_t pid = 0;
int r; int r;
assert(iovec_is_valid(payload)); assert(payload || payload_size == 0);
assert(iovec_is_valid(signature)); assert(signature || signature_size == 0);
r = pipe2(gpg_pipe, O_CLOEXEC); r = pipe2(gpg_pipe, O_CLOEXEC);
if (r < 0) if (r < 0)
return log_error_errno(errno, "Failed to create pipe for gpg: %m"); return log_error_errno(errno, "Failed to create pipe for gpg: %m");
if (iovec_is_set(signature)) { if (signature_size > 0) {
_cleanup_close_ int sig_file = -EBADF; _cleanup_close_ int sig_file = -EBADF;
sig_file = mkostemp(sig_file_path, O_RDWR); sig_file = mkostemp(sig_file_path, O_RDWR);
if (sig_file < 0) if (sig_file < 0)
return log_error_errno(errno, "Failed to create temporary file: %m"); return log_error_errno(errno, "Failed to create temporary file: %m");
r = loop_write(sig_file, signature->iov_base, signature->iov_len); r = loop_write(sig_file, signature, signature_size);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to write to temporary file: %m"); log_error_errno(r, "Failed to write to temporary file: %m");
goto finish; goto finish;
@ -487,13 +483,11 @@ static int verify_gpg(
gpg_pipe[0] = safe_close(gpg_pipe[0]); gpg_pipe[0] = safe_close(gpg_pipe[0]);
if (iovec_is_set(payload)) { r = loop_write(gpg_pipe[1], payload, payload_size);
r = loop_write(gpg_pipe[1], payload->iov_base, payload->iov_len);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to write to pipe: %m"); log_error_errno(r, "Failed to write to pipe: %m");
goto finish; goto finish;
} }
}
gpg_pipe[1] = safe_close(gpg_pipe[1]); gpg_pipe[1] = safe_close(gpg_pipe[1]);
@ -509,13 +503,14 @@ static int verify_gpg(
} }
finish: finish:
if (iovec_is_set(signature)) if (signature_size > 0)
(void) unlink(sig_file_path); (void) unlink(sig_file_path);
return r; return r;
} }
int pull_verify(ImportVerify verify, int pull_verify(ImportVerify verify,
const char *checksum, /* Verify with literal checksum */
PullJob *main_job, PullJob *main_job,
PullJob *checksum_job, PullJob *checksum_job,
PullJob *signature_job, PullJob *signature_job,
@ -531,13 +526,33 @@ int pull_verify(ImportVerify verify,
assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
assert((verify < 0) || !checksum);
assert(main_job); assert(main_job);
assert(main_job->state == PULL_JOB_DONE); assert(main_job->state == PULL_JOB_DONE);
if (verify < 0 || /* verification already done (via literal checksum) */ if (verify == IMPORT_VERIFY_NO) /* verification turned off */
verify == IMPORT_VERIFY_NO) /* verification turned off */
return 0; 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); r = import_url_last_component(main_job->url, &fn);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to extract filename from URL '%s': %m", main_job->url); return log_error_errno(r, "Failed to extract filename from URL '%s': %m", main_job->url);
@ -551,11 +566,11 @@ int pull_verify(ImportVerify verify,
verify_job = main_job; verify_job = main_job;
} else { } else {
assert(main_job->calc_checksum); assert(main_job->calc_checksum);
assert(iovec_is_set(&main_job->checksum)); assert(main_job->checksum);
assert(checksum_job); assert(checksum_job);
assert(checksum_job->state == PULL_JOB_DONE); assert(checksum_job->state == PULL_JOB_DONE);
if (!iovec_is_set(&checksum_job->payload)) if (!checksum_job->payload || checksum_job->payload_size <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
"Checksum is empty, cannot verify."); "Checksum is empty, cannot verify.");
@ -581,11 +596,11 @@ int pull_verify(ImportVerify verify,
assert(signature_job); assert(signature_job);
assert(signature_job->state == PULL_JOB_DONE); assert(signature_job->state == PULL_JOB_DONE);
if (!iovec_is_set(&signature_job->payload)) if (!signature_job->payload || signature_job->payload_size <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
"Signature is empty, cannot verify."); "Signature is empty, cannot verify.");
return verify_gpg(&verify_job->payload, &signature_job->payload); return verify_gpg(verify_job->payload, verify_job->payload_size, signature_job->payload, signature_job->payload_size);
} }
int verification_style_from_url(const char *url, VerificationStyle *ret) { int verification_style_from_url(const char *url, VerificationStyle *ret) {

View File

@ -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_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_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 *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 *checksum, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
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); 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);
typedef enum VerificationStyle { typedef enum VerificationStyle {
VERIFICATION_PER_FILE, /* SUSE-style ".sha256" files with detached gpg signature */ VERIFICATION_PER_FILE, /* SUSE-style ".sha256" files with detached gpg signature */

View File

@ -20,16 +20,6 @@
#include "time-util.h" #include "time-util.h"
#include "xattr-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) { void pull_job_close_disk_fd(PullJob *j) {
if (!j) if (!j)
return; return;
@ -57,9 +47,8 @@ PullJob* pull_job_unref(PullJob *j) {
free(j->url); free(j->url);
free(j->etag); free(j->etag);
strv_free(j->old_etags); strv_free(j->old_etags);
iovec_done(&j->payload); free(j->payload);
iovec_done(&j->checksum); free(j->checksum);
iovec_done(&j->expected_checksum);
return mfree(j); return mfree(j);
} }
@ -95,16 +84,15 @@ static int pull_job_restart(PullJob *j, const char *new_url) {
j->state = PULL_JOB_INIT; j->state = PULL_JOB_INIT;
j->error = 0; j->error = 0;
iovec_done(&j->payload); j->payload = mfree(j->payload);
j->payload_size = 0;
j->written_compressed = 0; j->written_compressed = 0;
j->written_uncompressed = 0; j->written_uncompressed = 0;
j->content_length = UINT64_MAX; j->content_length = UINT64_MAX;
j->etag = mfree(j->etag); j->etag = mfree(j->etag);
j->etag_exists = false; j->etag_exists = false;
j->mtime = 0; j->mtime = 0;
iovec_done(&j->checksum); j->checksum = mfree(j->checksum);
iovec_done(&j->expected_checksum);
j->expected_content_length = UINT64_MAX;
curl_glue_remove_and_free(j->glue, j->curl); curl_glue_remove_and_free(j->glue, j->curl);
j->curl = NULL; j->curl = NULL;
@ -126,15 +114,6 @@ static int pull_job_restart(PullJob *j, const char *new_url) {
return 0; 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) { void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
PullJob *j = NULL; PullJob *j = NULL;
char *scheme = NULL; char *scheme = NULL;
@ -187,7 +166,7 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
goto finish; goto finish;
} }
if (http_status_etag_exists(status)) { if (status == 304) {
log_info("Image already downloaded. Skipping download."); log_info("Image already downloaded. Skipping download.");
j->etag_exists = true; j->etag_exists = true;
r = 0; r = 0;
@ -235,46 +214,30 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
goto finish; goto finish;
} }
uint64_t cl = pull_job_content_length_effective(j); if (j->content_length != UINT64_MAX &&
if (cl != UINT64_MAX && j->content_length != j->written_compressed) {
cl != j->written_compressed) {
r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Download truncated."); r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Download truncated.");
goto finish; goto finish;
} }
if (j->checksum_ctx) { if (j->checksum_ctx) {
unsigned checksum_len; unsigned checksum_len;
uint8_t k[EVP_MAX_MD_SIZE];
iovec_done(&j->checksum); r = EVP_DigestFinal_ex(j->checksum_ctx, k, &checksum_len);
j->checksum.iov_base = malloc(EVP_MAX_MD_SIZE);
if (!j->checksum.iov_base) {
r = log_oom();
goto finish;
}
r = EVP_DigestFinal_ex(j->checksum_ctx, j->checksum.iov_base, &checksum_len);
if (r == 0) { if (r == 0) {
r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum."); r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum.");
goto finish; goto finish;
} }
assert(checksum_len <= EVP_MAX_MD_SIZE); assert(checksum_len <= sizeof k);
j->checksum.iov_len = checksum_len;
if (DEBUG_LOGGING) { j->checksum = hexmem(k, checksum_len);
_cleanup_free_ char *h = hexmem(j->checksum.iov_base, j->checksum.iov_len); if (!j->checksum) {
if (!h) {
r = log_oom(); r = log_oom();
goto finish; goto finish;
} }
log_debug("%s of %s is %s.", EVP_MD_CTX_get0_name(j->checksum_ctx), j->url, h); log_debug("SHA256 of %s is %s.", j->url, j->checksum);
}
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 /* Do a couple of finishing disk operations, but only if we are the sole owner of the file (i.e. no
@ -380,14 +343,11 @@ static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata)
} }
if (j->disk_fd < 0 || j->force_memory) { if (j->disk_fd < 0 || j->force_memory) {
uint8_t *a = j->payload.iov_base; if (!GREEDY_REALLOC(j->payload, j->payload_size + sz))
if (!GREEDY_REALLOC(a, j->payload.iov_len + sz + 1))
return log_oom(); return log_oom();
*((uint8_t*) mempcpy(a + j->payload.iov_len, p, sz)) = 0; memcpy(j->payload + j->payload_size, p, sz);
j->payload.iov_base = a; j->payload_size += sz;
j->payload.iov_len += sz;
} }
j->written_uncompressed += sz; j->written_uncompressed += sz;
@ -399,39 +359,38 @@ finish:
return 0; return 0;
} }
static int pull_job_write_compressed(PullJob *j, const struct iovec *data) { static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) {
int r; int r;
assert(j); assert(j);
assert(iovec_is_valid(data)); assert(p);
if (!iovec_is_set(data)) if (sz <= 0)
return 0; return 0;
if (j->written_compressed + data->iov_len < j->written_compressed) if (j->written_compressed + sz < j->written_compressed)
return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow"); return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
if (j->written_compressed + data->iov_len > j->compressed_max) if (j->written_compressed + sz > j->compressed_max)
return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing."); return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing.");
uint64_t cl = pull_job_content_length_effective(j); if (j->content_length != UINT64_MAX &&
if (cl != UINT64_MAX && j->written_compressed + sz > j->content_length)
j->written_compressed + data->iov_len > cl)
return log_error_errno(SYNTHETIC_ERRNO(EFBIG), return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
"Content length incorrect."); "Content length incorrect.");
if (j->checksum_ctx) { if (j->checksum_ctx) {
r = EVP_DigestUpdate(j->checksum_ctx, data->iov_base, data->iov_len); r = EVP_DigestUpdate(j->checksum_ctx, p, sz);
if (r == 0) if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Could not hash chunk."); "Could not hash chunk.");
} }
r = import_uncompress(&j->compress, data->iov_base, data->iov_len, pull_job_write_uncompressed, j); r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j);
if (r < 0) if (r < 0)
return r; return r;
j->written_compressed += data->iov_len; j->written_compressed += sz;
return 0; return 0;
} }
@ -472,11 +431,14 @@ static int pull_job_open_disk(PullJob *j) {
} }
static int pull_job_detect_compression(PullJob *j) { static int pull_job_detect_compression(PullJob *j) {
_cleanup_free_ uint8_t *stub = NULL;
size_t stub_size;
int r; int r;
assert(j); assert(j);
r = import_uncompress_detect(&j->compress, j->payload.iov_base, j->payload.iov_len); r = import_uncompress_detect(&j->compress, j->payload, j->payload_size);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to initialize compressor: %m"); return log_error_errno(r, "Failed to initialize compressor: %m");
if (r == 0) if (r == 0)
@ -489,11 +451,15 @@ static int pull_job_detect_compression(PullJob *j) {
return r; return r;
/* Now, take the payload we read so far, and decompress it */ /* Now, take the payload we read so far, and decompress it */
_cleanup_(iovec_done) struct iovec stub = TAKE_STRUCT(j->payload); stub = j->payload;
stub_size = j->payload_size;
j->payload = NULL;
j->payload_size = 0;
j->state = PULL_JOB_RUNNING; j->state = PULL_JOB_RUNNING;
r = pull_job_write_compressed(j, &stub); r = pull_job_write_compressed(j, stub, stub_size);
if (r < 0) if (r < 0)
return r; return r;
@ -511,11 +477,15 @@ static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb,
case PULL_JOB_ANALYZING: case PULL_JOB_ANALYZING:
/* Let's first check what it actually is */ /* Let's first check what it actually is */
if (!iovec_append(&j->payload, &IOVEC_MAKE(contents, sz))) {
if (!GREEDY_REALLOC(j->payload, j->payload_size + sz)) {
r = log_oom(); r = log_oom();
goto fail; goto fail;
} }
memcpy(j->payload + j->payload_size, contents, sz);
j->payload_size += sz;
r = pull_job_detect_compression(j); r = pull_job_detect_compression(j);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -523,7 +493,8 @@ static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb,
break; break;
case PULL_JOB_RUNNING: case PULL_JOB_RUNNING:
r = pull_job_write_compressed(j, &IOVEC_MAKE(contents, sz));
r = pull_job_write_compressed(j, contents, sz);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -545,6 +516,16 @@ fail:
return 0; 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) { 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; _cleanup_free_ char *length = NULL, *last_modified = NULL, *etag = NULL;
size_t sz = size * nmemb; size_t sz = size * nmemb;
@ -608,12 +589,6 @@ static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb
goto fail; 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); log_info("Downloading %s for %s.", FORMAT_BYTES(j->content_length), j->url);
} }
@ -716,7 +691,6 @@ int pull_job_new(
.url = TAKE_PTR(u), .url = TAKE_PTR(u),
.offset = UINT64_MAX, .offset = UINT64_MAX,
.sync = true, .sync = true,
.expected_content_length = UINT64_MAX,
}; };
*ret = TAKE_PTR(j); *ret = TAKE_PTR(j);

View File

@ -58,9 +58,8 @@ typedef struct PullJob {
uint64_t uncompressed_max; uint64_t uncompressed_max;
uint64_t compressed_max; uint64_t compressed_max;
uint64_t expected_content_length; uint8_t *payload;
size_t payload_size;
struct iovec payload;
int disk_fd; int disk_fd;
bool close_disk_fd; bool close_disk_fd;
@ -77,9 +76,7 @@ typedef struct PullJob {
bool calc_checksum; bool calc_checksum;
EVP_MD_CTX *checksum_ctx; EVP_MD_CTX *checksum_ctx;
struct iovec checksum; char *checksum;
struct iovec expected_checksum;
bool sync; bool sync;
bool force_memory; bool force_memory;
} PullJob; } PullJob;

View File

@ -67,6 +67,8 @@ typedef struct RawPull {
char *verity_path; char *verity_path;
char *verity_temp_path; char *verity_temp_path;
char *checksum;
} RawPull; } RawPull;
RawPull* raw_pull_unref(RawPull *i) { RawPull* raw_pull_unref(RawPull *i) {
@ -97,6 +99,7 @@ RawPull* raw_pull_unref(RawPull *i) {
free(i->verity_path); free(i->verity_path);
free(i->image_root); free(i->image_root);
free(i->local); free(i->local);
free(i->checksum);
return mfree(i); return mfree(i);
} }
@ -500,10 +503,13 @@ static int raw_pull_rename_auxiliary_file(
} }
static void raw_pull_job_on_finished(PullJob *j) { static void raw_pull_job_on_finished(PullJob *j) {
RawPull *i;
int r; int r;
assert(j); assert(j);
RawPull *i = ASSERT_PTR(j->userdata); assert(j->userdata);
i = j->userdata;
if (j->error != 0) { if (j->error != 0) {
/* Only the main job and the checksum job are fatal if they fail. The other fails are just /* Only the main job and the checksum job are fatal if they fail. The other fails are just
@ -579,6 +585,7 @@ static void raw_pull_job_on_finished(PullJob *j) {
raw_pull_report_progress(i, RAW_VERIFYING); raw_pull_report_progress(i, RAW_VERIFYING);
r = pull_verify(i->verify, r = pull_verify(i->verify,
i->checksum,
i->raw_job, i->raw_job,
i->checksum_job, i->checksum_job,
i->signature_job, i->signature_job,
@ -820,7 +827,7 @@ int raw_pull_start(
uint64_t size_max, uint64_t size_max,
ImportFlags flags, ImportFlags flags,
ImportVerify verify, ImportVerify verify,
const struct iovec *checksum) { const char *checksum) {
int r; int r;
@ -828,11 +835,11 @@ int raw_pull_start(
assert(url); assert(url);
assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
assert((verify < 0) || !iovec_is_set(checksum)); assert((verify < 0) || !checksum);
assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_RAW)); assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_RAW));
assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT)); 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)) || !(flags & IMPORT_DIRECT));
assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !iovec_is_set(checksum)); assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !checksum);
if (!http_url_is_valid(url) && !file_url_is_valid(url)) if (!http_url_is_valid(url) && !file_url_is_valid(url))
return -EINVAL; return -EINVAL;
@ -847,6 +854,10 @@ int raw_pull_start(
if (r < 0) if (r < 0)
return r; return r;
r = free_and_strdup(&i->checksum, checksum);
if (r < 0)
return r;
i->flags = flags; i->flags = flags;
i->verify = verify; i->verify = verify;
@ -858,12 +869,9 @@ int raw_pull_start(
i->raw_job->on_finished = raw_pull_job_on_finished; i->raw_job->on_finished = raw_pull_job_on_finished;
i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw; i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
if (iovec_is_set(checksum)) { if (checksum)
if (!iovec_memdup(checksum, &i->raw_job->expected_checksum))
return -ENOMEM;
i->raw_job->calc_checksum = true; 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 /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its
* signature, which we let gpg verify instead. */ * signature, which we let gpg verify instead. */
@ -872,7 +880,7 @@ int raw_pull_start(
return r; return r;
i->raw_job->calc_checksum = r; i->raw_job->calc_checksum = r;
i->raw_job->force_memory = !r; /* make sure this is both written to disk if that's 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 */ * requested and into memory, since we need to verify it */
} }
@ -891,6 +899,7 @@ int raw_pull_start(
&i->checksum_job, &i->checksum_job,
&i->signature_job, &i->signature_job,
verify, verify,
i->checksum,
url, url,
i->glue, i->glue,
raw_pull_job_on_finished, raw_pull_job_on_finished,

View File

@ -14,4 +14,4 @@ RawPull* raw_pull_unref(RawPull *pull);
DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref); 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 struct iovec *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 char *checksum);

View File

@ -65,6 +65,8 @@ typedef struct TarPull {
char *settings_path; char *settings_path;
char *settings_temp_path; char *settings_temp_path;
char *checksum;
int tree_fd; int tree_fd;
int userns_fd; int userns_fd;
@ -96,6 +98,7 @@ TarPull* tar_pull_unref(TarPull *i) {
free(i->settings_path); free(i->settings_path);
free(i->image_root); free(i->image_root);
free(i->local); free(i->local);
free(i->checksum);
safe_close(i->tree_fd); safe_close(i->tree_fd);
safe_close(i->userns_fd); safe_close(i->userns_fd);
@ -399,10 +402,13 @@ static bool tar_pull_is_done(TarPull *i) {
} }
static void tar_pull_job_on_finished(PullJob *j) { static void tar_pull_job_on_finished(PullJob *j) {
TarPull *i;
int r; int r;
assert(j); assert(j);
TarPull *i = ASSERT_PTR(j->userdata); assert(j->userdata);
i = j->userdata;
if (j->error != 0) { if (j->error != 0) {
clear_progress_bar(/* prefix= */ NULL); clear_progress_bar(/* prefix= */ NULL);
@ -472,6 +478,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
clear_progress_bar(/* prefix= */ NULL); clear_progress_bar(/* prefix= */ NULL);
r = pull_verify(i->verify, r = pull_verify(i->verify,
i->checksum,
i->tar_job, i->tar_job,
i->checksum_job, i->checksum_job,
i->signature_job, i->signature_job,
@ -579,11 +586,13 @@ finish:
static int tar_pull_job_on_open_disk_tar(PullJob *j) { static int tar_pull_job_on_open_disk_tar(PullJob *j) {
const char *where; const char *where;
TarPull *i;
int r; int r;
assert(j); assert(j);
assert(j->userdata);
TarPull *i = ASSERT_PTR(j->userdata); i = j->userdata;
assert(i->tar_job == j); assert(i->tar_job == j);
assert(!pidref_is_set(&i->tar_pid)); assert(!pidref_is_set(&i->tar_pid));
assert(i->tree_fd < 0); assert(i->tree_fd < 0);
@ -689,17 +698,17 @@ int tar_pull_start(
const char *local, const char *local,
ImportFlags flags, ImportFlags flags,
ImportVerify verify, ImportVerify verify,
const struct iovec *checksum) { const char *checksum) {
int r; int r;
assert(i); assert(i);
assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
assert((verify < 0) || !iovec_is_set(checksum)); assert((verify < 0) || !checksum);
assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_TAR)); assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_TAR));
assert(!(flags & IMPORT_PULL_SETTINGS) || !(flags & IMPORT_DIRECT)); assert(!(flags & IMPORT_PULL_SETTINGS) || !(flags & IMPORT_DIRECT));
assert(!(flags & IMPORT_PULL_SETTINGS) || !iovec_is_set(checksum)); assert(!(flags & IMPORT_PULL_SETTINGS) || !checksum);
if (!http_url_is_valid(url) && !file_url_is_valid(url)) if (!http_url_is_valid(url) && !file_url_is_valid(url))
return -EINVAL; return -EINVAL;
@ -714,6 +723,10 @@ int tar_pull_start(
if (r < 0) if (r < 0)
return r; return r;
r = free_and_strdup(&i->checksum, checksum);
if (r < 0)
return r;
i->flags = flags; i->flags = flags;
i->verify = verify; i->verify = verify;
@ -724,14 +737,7 @@ int tar_pull_start(
i->tar_job->on_finished = tar_pull_job_on_finished; 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->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)) { if (!FLAGS_SET(flags, IMPORT_DIRECT)) {
r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
@ -744,6 +750,7 @@ int tar_pull_start(
&i->checksum_job, &i->checksum_job,
&i->signature_job, &i->signature_job,
verify, verify,
checksum,
url, url,
i->glue, i->glue,
tar_pull_job_on_finished, tar_pull_job_on_finished,

View File

@ -14,4 +14,4 @@ TarPull* tar_pull_unref(TarPull *pull);
DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref); 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 struct iovec *checksum); int tar_pull_start(TarPull *pull, const char *url, const char *local, ImportFlags flags, ImportVerify verify, const char *checksum);

View File

@ -15,7 +15,6 @@
#include "import-common.h" #include "import-common.h"
#include "import-util.h" #include "import-util.h"
#include "io-util.h" #include "io-util.h"
#include "iovec-util.h"
#include "log.h" #include "log.h"
#include "main-func.h" #include "main-func.h"
#include "parse-argument.h" #include "parse-argument.h"
@ -33,11 +32,11 @@ static char *arg_image_root = NULL;
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; 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 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 uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
static struct iovec arg_checksum = {}; static char *arg_checksum = NULL;
static ImageClass arg_class = IMAGE_MACHINE; static ImageClass arg_class = IMAGE_MACHINE;
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID; static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
STATIC_DESTRUCTOR_REGISTER(arg_checksum, iovec_done); STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_root, freep);
static int normalize_local(const char *local, const char *url, char **ret) { static int normalize_local(const char *local, const char *url, char **ret) {
@ -163,7 +162,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
normalized, normalized,
arg_import_flags & IMPORT_PULL_FLAGS_MASK_TAR, arg_import_flags & IMPORT_PULL_FLAGS_MASK_TAR,
arg_verify, arg_verify,
&arg_checksum); arg_checksum);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to pull image: %m"); return log_error_errno(r, "Failed to pull image: %m");
@ -232,7 +231,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
arg_size_max, arg_size_max,
arg_import_flags & IMPORT_PULL_FLAGS_MASK_RAW, arg_import_flags & IMPORT_PULL_FLAGS_MASK_RAW,
arg_verify, arg_verify,
&arg_checksum); arg_checksum);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to pull image: %m"); return log_error_errno(r, "Failed to pull image: %m");
@ -372,6 +371,7 @@ static int parse_argv(int argc, char *argv[]) {
v = import_verify_from_string(optarg); v = import_verify_from_string(optarg);
if (v < 0) { if (v < 0) {
_cleanup_free_ void *h = NULL; _cleanup_free_ void *h = NULL;
char *hh;
size_t n; size_t n;
/* If this is not a valid verification mode, maybe it's a literally specified /* If this is not a valid verification mode, maybe it's a literally specified
@ -385,10 +385,11 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"64 hex character SHA256 hash required when specifying explicit checksum, %zu specified", n * 2); "64 hex character SHA256 hash required when specifying explicit checksum, %zu specified", n * 2);
iovec_done(&arg_checksum); hh = hexmem(h, n); /* bring into canonical (lowercase) form */
arg_checksum.iov_base = TAKE_PTR(h); if (!hh)
arg_checksum.iov_len = n; return log_oom();
free_and_replace(arg_checksum, hh);
arg_import_flags &= ~(IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY); arg_import_flags &= ~(IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY);
arg_verify = _IMPORT_VERIFY_INVALID; arg_verify = _IMPORT_VERIFY_INVALID;
} else } else
@ -541,7 +542,7 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT)) 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."); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
if (iovec_is_set(&arg_checksum) && (arg_import_flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) != 0) if (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."); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Literal checksum verification only supported if no associated files are downloaded.");
if (!arg_image_root) { if (!arg_image_root) {

View File

@ -15,44 +15,30 @@
#include "process-util.h" #include "process-util.h"
#include "string-util.h" #include "string-util.h"
/* Maximum namespace name length */
#define NAMESPACE_NAME_MAX 16U
/* So the namespace name should be 16 chars at max (because we want that it is usable in usernames, which
* have a limit of 31 chars effectively, and the nsresourced service wants to prefix/suffix some bits). But
* it also should be unique if we are called multiple times in a row. Hence we take the "comm" name (which is
* 15 chars), and suffix it with the PID and a counter, possibly overriding the end. */
assert_cc(TASK_COMM_LEN == NAMESPACE_NAME_MAX);
static int make_pid_name(char **ret) { static int make_pid_name(char **ret) {
char comm[TASK_COMM_LEN]; char comm[TASK_COMM_LEN];
static uint64_t counter = 0;
assert(ret); assert(ret);
if (prctl(PR_GET_NAME, comm) < 0) if (prctl(PR_GET_NAME, comm) < 0)
return -errno; return -errno;
/* So the namespace name should be 16 chars at max (because we want that it is usable in usernames,
* which have a limit of 31 chars effectively, and the nsresourced service wants to prefix/suffix
* some bits). But it also should be unique if we are called multiple times in a row. Hence we take
* the "comm" name (which is 15 chars), and suffix it with the PID, possibly overriding the end. */
assert_cc(TASK_COMM_LEN == 15 + 1);
char spid[DECIMAL_STR_MAX(pid_t)]; char spid[DECIMAL_STR_MAX(pid_t)];
xsprintf(spid, PID_FMT, getpid_cached()); xsprintf(spid, PID_FMT, getpid_cached());
/* Include a counter in the name, so that we can allocate multiple namespaces per process, with assert(strlen(spid) <= 16);
* unique names. For the first namespace we suppress the suffix */ strshorten(comm, 16 - strlen(spid));
char scounter[sizeof(counter) * 2 + 1];
if (counter == 0)
scounter[0] = 0;
else
xsprintf(scounter, "%" PRIx64, counter);
counter++;
strshorten(comm, LESS_BY(NAMESPACE_NAME_MAX, strlen(spid) + strlen(scounter))); _cleanup_free_ char *s = strjoin(comm, spid);
_cleanup_free_ char *s = strjoin(comm, spid, scounter);
if (!s) if (!s)
return -ENOMEM; return -ENOMEM;
strshorten(s, NAMESPACE_NAME_MAX);
*ret = TAKE_PTR(s); *ret = TAKE_PTR(s);
return 0; return 0;
} }