mirror of
https://github.com/systemd/systemd
synced 2025-11-09 20:04:46 +01:00
Compare commits
11 Commits
f8293452b6
...
aae054e1ab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aae054e1ab | ||
|
|
14d9f58823 | ||
|
|
729e1f24c5 | ||
|
|
33f49312f8 | ||
|
|
f56531c25c | ||
|
|
7aa847933f | ||
|
|
5bba33f5e9 | ||
|
|
6f16ab4893 | ||
|
|
e825635af6 | ||
|
|
cb1caab98e | ||
|
|
bf51a545a4 |
@ -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) {
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -15,30 +15,44 @@
|
||||
#include "process-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) {
|
||||
char comm[TASK_COMM_LEN];
|
||||
static uint64_t counter = 0;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (prctl(PR_GET_NAME, comm) < 0)
|
||||
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)];
|
||||
xsprintf(spid, PID_FMT, getpid_cached());
|
||||
|
||||
assert(strlen(spid) <= 16);
|
||||
strshorten(comm, 16 - strlen(spid));
|
||||
/* Include a counter in the name, so that we can allocate multiple namespaces per process, with
|
||||
* unique names. For the first namespace we suppress the suffix */
|
||||
char scounter[sizeof(counter) * 2 + 1];
|
||||
if (counter == 0)
|
||||
scounter[0] = 0;
|
||||
else
|
||||
xsprintf(scounter, "%" PRIx64, counter);
|
||||
counter++;
|
||||
|
||||
_cleanup_free_ char *s = strjoin(comm, spid);
|
||||
strshorten(comm, LESS_BY(NAMESPACE_NAME_MAX, strlen(spid) + strlen(scounter)));
|
||||
|
||||
_cleanup_free_ char *s = strjoin(comm, spid, scounter);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
strshorten(s, NAMESPACE_NAME_MAX);
|
||||
|
||||
*ret = TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user