1
0
mirror of https://github.com/systemd/systemd synced 2025-11-09 20:04:46 +01:00

Compare commits

..

11 Commits

Author SHA1 Message Date
Lennart Poettering
aae054e1ab nsresource: allow multiple userns from the same process in parallel
When generating a name for a transient userns automatically we so far
just included our PID to make it unique. That doens't really work if
multiple userns shall be kept in parallel by a single process. Let's hence
include a counter as well.
2025-11-08 18:32:37 +01:00
Lennart Poettering
14d9f58823
import: various smaller tweaks and fixes, preparation for OCI download support in importd (#39620) 2025-11-08 18:31:11 +01:00
Lennart Poettering
729e1f24c5 pull-job: port .payload field to struct iovec
struct iovec is really how we should encode any form of arbitrary blob
data
2025-11-08 09:28:50 +01:00
Lennart Poettering
33f49312f8 pull-job: use http_status_etag_exists() at a second place 2025-11-08 09:28:50 +01:00
Lennart Poettering
f56531c25c pull: there's no need to keep the downloaded image in memory, except for the sha256sums/gpg file
This seems to be a mistake, in place since the first commit: we only
want the downloaded data in memory if this is a sha256sums or gpg file,
which we need to prorcess ourselves.
2025-11-08 09:28:50 +01:00
Lennart Poettering
7aa847933f pull: use ASSERT_PTR() to shorten code a tiny bit 2025-11-08 09:28:50 +01:00
Lennart Poettering
5bba33f5e9 pull: now that PullJob can verify expected digests, let's rely on it for tar/raw pulling
Instead of authenticating the downloaded image explicity in the tar and
in the raw downloader, we can now rely on the checksum checking in the
generic PullJob code. Hence do so: drop tep the checksum field from
TarPull and RawPull, and just initialized the ->expected_checksum in the
relevant PullJob instead.
2025-11-08 09:28:50 +01:00
Lennart Poettering
6f16ab4893 pull-job: optionally store an expected checksum in PullJob object 2025-11-08 09:28:50 +01:00
Lennart Poettering
e825635af6 import: rework pull logic to store download digests in binary form rather than string
We generally want to store data in parsed form, not formatted form,
hence let's follow our own rules on this, and store the message digest
as "struct iovec" rather than as string. This is generally more
efficient and safer, simply because of case issues.
2025-11-08 09:28:50 +01:00
Lennart Poettering
cb1caab98e pull-job: optionally take expected content length and compare it with what we are downloading 2025-11-08 09:27:46 +01:00
Lennart Poettering
bf51a545a4 pull-job: always implicitly NUL terminate downloaded payload stored in memory
Just as a safety measure, let's always NUL terminate what we are
downloading, maybe future code will parse it as string, and is sloppy by
accident.

(We have similar logic in read_full_file(), and I think it's a really
good rule, to always implicitly NUL terminate blobs we acquire that
might very well be used as text later on)
2025-11-08 09:27:46 +01:00
10 changed files with 182 additions and 171 deletions

View File

@ -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) {

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_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 */

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}