mirror of
https://github.com/systemd/systemd
synced 2026-03-16 10:04:47 +01:00
Compare commits
4 Commits
e6a25c2fc7
...
d0badc0a61
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0badc0a61 | ||
|
|
ba1a9c7b72 | ||
|
|
f6edc81230 | ||
|
|
2f7fdef55e |
@ -836,3 +836,10 @@ Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as
|
||||
overall number of threads used to load modules by `systemd-modules-load`.
|
||||
If unset, the default number of threads is equal to the number of online CPUs,
|
||||
with a maximum of 16. If set to `0`, multi-threaded loading is disabled.
|
||||
|
||||
`systemd-sysupdate`:
|
||||
|
||||
* `$SYSTEMD_SYSUPDATE_VERIFY_FRESHNESS` – takes a boolean. If false the
|
||||
'freshness' check via `BEST-BEFORE-YYYY-MM-DD` files in `SHA256SUMS` manifest
|
||||
files is disabled, and updating from outdated manifests will not result in an
|
||||
error.
|
||||
|
||||
@ -311,6 +311,16 @@
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>The <filename>SHA256SUMS</filename> manifest files used by <literal>url-file</literal> and
|
||||
<literal>url-tar</literal> resource types follow the usual file format generated by GNU's <citerefentry
|
||||
project='man-pages'><refentrytitle>sha256sum</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
tool. It is recommended to use <option>--binary</option> mode, even if that has no real effect on Linux
|
||||
systems. The listing should only contain ASCII characters, and only regular file names (i.e. no absolute
|
||||
or relative paths). If the <filename>SHA256SUMS</filename> listing contains a special file
|
||||
<literal>BEST-BEFORE-YYYY-MM-DD</literal> (with the year, month, day filled in), then the file listing
|
||||
will not be considered valid past the specified date, and the transfer will fail in such a case. This may
|
||||
be used to detect "freshness" of the manifest file.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
||||
@ -16,12 +16,12 @@
|
||||
#include "efi-fundamental.h"
|
||||
#include "efivars.h"
|
||||
#include "env-file.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "glyph-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "install-file.h"
|
||||
#include "io-util.h"
|
||||
#include "kernel-config.h"
|
||||
#include "log.h"
|
||||
@ -597,17 +597,12 @@ static int install_entry_token(void) {
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
static int efi_timestamp(EFI_TIME *ret) {
|
||||
uint64_t epoch = UINT64_MAX;
|
||||
struct tm tm = {};
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch);
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
|
||||
|
||||
r = localtime_or_gmtime_usec(epoch != UINT64_MAX ? epoch : now(CLOCK_REALTIME), /* utc= */ true, &tm);
|
||||
r = localtime_or_gmtime_usec(source_date_epoch_or_now(), /* utc= */ true, &tm);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to convert timestamp to calendar time: %m");
|
||||
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
#include "dirent-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "efivars.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "factory-reset.h"
|
||||
@ -48,6 +47,7 @@
|
||||
#include "id128-util.h"
|
||||
#include "image-policy.h"
|
||||
#include "initrd-util.h"
|
||||
#include "install-file.h"
|
||||
#include "io-util.h"
|
||||
#include "json-util.h"
|
||||
#include "libmount-util.h"
|
||||
@ -6275,30 +6275,6 @@ static int make_subvolumes_by_source_inode_hashmap(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static usec_t epoch_or_infinity(void) {
|
||||
static usec_t cache;
|
||||
static bool cached = false;
|
||||
uint64_t epoch;
|
||||
int r;
|
||||
|
||||
if (cached)
|
||||
return cache;
|
||||
|
||||
r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch);
|
||||
if (r >= 0) {
|
||||
if (epoch <= UINT64_MAX / USEC_PER_SEC) { /* Overflow check */
|
||||
cached = true;
|
||||
return (cache = epoch * USEC_PER_SEC);
|
||||
}
|
||||
r = -ERANGE;
|
||||
}
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
|
||||
|
||||
cached = true;
|
||||
return (cache = USEC_INFINITY);
|
||||
}
|
||||
|
||||
static int file_is_denylisted(const char *source, Hashmap *denylist) {
|
||||
_cleanup_close_ int pfd = -EBADF;
|
||||
struct stat st, rst;
|
||||
@ -6391,7 +6367,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *subvolumes_by_source_inode = NULL;
|
||||
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
|
||||
usec_t ts = epoch_or_infinity();
|
||||
usec_t ts = parse_source_date_epoch();
|
||||
|
||||
r = make_copy_files_denylist(context, p, line->source, line->target, &denylist);
|
||||
if (r < 0)
|
||||
@ -6532,7 +6508,7 @@ static int do_make_directories(Partition *p, const char *root) {
|
||||
}
|
||||
|
||||
STRV_FOREACH(d, override_dirs ?: p->make_directories) {
|
||||
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), subvolumes);
|
||||
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, parse_source_date_epoch(), subvolumes);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
|
||||
}
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
#include "build.h"
|
||||
#include "copy.h"
|
||||
#include "efi-fundamental.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "install-file.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
@ -293,16 +293,12 @@ static int spc_indirect_data_content_new(const void *digest, size_t digestsz, ui
|
||||
|
||||
static int asn1_timestamp(ASN1_TIME **ret) {
|
||||
ASN1_TIME *time;
|
||||
uint64_t epoch = UINT64_MAX;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch);
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
|
||||
usec_t epoch = parse_source_date_epoch();
|
||||
|
||||
if (epoch == UINT64_MAX) {
|
||||
if (epoch == USEC_INFINITY) {
|
||||
time = X509_gmtime_adj(NULL, 0);
|
||||
if (!time)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get current time: %s",
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include "btrfs-util.h"
|
||||
#include "chattr-util.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
@ -13,6 +14,7 @@
|
||||
#include "log.h"
|
||||
#include "rm-rf.h"
|
||||
#include "sync-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
static int fs_make_very_read_only(int fd) {
|
||||
struct stat st;
|
||||
@ -301,3 +303,38 @@ int install_file(int source_atfd, const char *source_name,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
usec_t parse_source_date_epoch(void) {
|
||||
static usec_t cache;
|
||||
static bool cached = false;
|
||||
int r;
|
||||
|
||||
if (cached)
|
||||
return cache;
|
||||
|
||||
uint64_t t;
|
||||
r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &t);
|
||||
if (r >= 0) {
|
||||
if (MUL_SAFE(&cache, t, USEC_PER_SEC)) {
|
||||
cached = true;
|
||||
return cache;
|
||||
}
|
||||
|
||||
r = -ERANGE;
|
||||
}
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
|
||||
|
||||
cached = true;
|
||||
return (cache = USEC_INFINITY);
|
||||
}
|
||||
|
||||
usec_t source_date_epoch_or_now(void) {
|
||||
usec_t epoch;
|
||||
|
||||
epoch = parse_source_date_epoch();
|
||||
if (epoch != USEC_INFINITY)
|
||||
return epoch;
|
||||
|
||||
return now(CLOCK_REALTIME);
|
||||
}
|
||||
|
||||
@ -13,3 +13,6 @@ typedef enum InstallFileFlags {
|
||||
} InstallFileFlags;
|
||||
|
||||
int install_file(int source_atfd, const char *source_name, int target_atfd, const char *target_name, InstallFileFlags flags);
|
||||
|
||||
usec_t parse_source_date_epoch(void);
|
||||
usec_t source_date_epoch_or_now(void);
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "device-util.h"
|
||||
#include "devnum-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fdisk-util.h"
|
||||
@ -21,6 +22,7 @@
|
||||
#include "gpt.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "import-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "sort-util.h"
|
||||
@ -351,6 +353,82 @@ static int download_manifest(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_magic_file(
|
||||
const char *fn,
|
||||
const struct iovec *hash) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(fn);
|
||||
assert(iovec_is_set(hash));
|
||||
|
||||
/* Validates "BEST-BEFORE-*" magic files we find in SHA256SUMS manifests. For now we ignore the
|
||||
* contents of such files (which might change one day), and only look at the file name.
|
||||
*
|
||||
* Note that if multiple BEST-BEFORE-* files exist in the same listing we'll honour them all, and
|
||||
* fail whenever *any* of them indicate a date that's already in the past. */
|
||||
|
||||
const char *e = startswith(fn, "BEST-BEFORE-");
|
||||
if (!e)
|
||||
return 0;
|
||||
|
||||
/* SHA256 hash of an empty file */
|
||||
static const uint8_t expected_hash[] = {
|
||||
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
|
||||
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
||||
};
|
||||
|
||||
/* Even if we ignore if people have non-empty files for this file, let's nonetheless warn about it,
|
||||
* so that people fix it. After all we want to retain liberty to maybe one day place some useful data
|
||||
* inside it */
|
||||
if (iovec_memcmp(&IOVEC_MAKE(expected_hash, sizeof(expected_hash)), hash) != 0)
|
||||
log_warning("Hash of best before marker file '%s' has unexpected value, ignoring.", fn);
|
||||
|
||||
struct tm parsed_tm = {};
|
||||
const char *n = strptime(e, "%Y-%m-%d", &parsed_tm);
|
||||
if (!n || *n != 0) {
|
||||
/* Doesn't parse? Then it's not a best-before date */
|
||||
log_warning("Found best before marker with an invalid date, ignoring: %s", fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct tm copy_tm = parsed_tm;
|
||||
usec_t best_before;
|
||||
r = mktime_or_timegm_usec(©_tm, /* utc= */ true, &best_before);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to convert best before time: %m");
|
||||
if (copy_tm.tm_mday != parsed_tm.tm_mday ||
|
||||
copy_tm.tm_mon != parsed_tm.tm_mon ||
|
||||
copy_tm.tm_year != parsed_tm.tm_year) {
|
||||
/* date was not normalized? (e.g. "30th of feb") */
|
||||
log_warning("Found best before marker with a non-normalized data, ignoring: %s", fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
usec_t nw = now(CLOCK_REALTIME);
|
||||
if (best_before < nw) {
|
||||
/* We are past the best before date! Yikes! */
|
||||
|
||||
r = secure_getenv_bool("SYSTEMD_SYSUPDATE_VERIFY_FRESHNESS");
|
||||
if (r < 0 && r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SYSTEMD_SYSUPDATE_VERIFY_FRESHNESS, ignoring: %m");
|
||||
|
||||
if (r == 0) {
|
||||
log_warning("Best before marker indicates out-of-date file list, but told to ignore this, hence ignoring (%s < %s).",
|
||||
FORMAT_TIMESTAMP(best_before), FORMAT_TIMESTAMP(nw));
|
||||
return 1; /* we processed this line, don't use for pattern matching */
|
||||
}
|
||||
|
||||
return log_error_errno(
|
||||
SYNTHETIC_ERRNO(ESTALE),
|
||||
"Best before marker indicates out-of-date file list, refusing (%s < %s).",
|
||||
FORMAT_TIMESTAMP(best_before), FORMAT_TIMESTAMP(nw));
|
||||
}
|
||||
|
||||
log_info("Found best before marker, and it checks out, proceeding.");
|
||||
return 1; /* we processed this line, don't use for pattern matching */
|
||||
}
|
||||
|
||||
static int resource_load_from_web(
|
||||
Resource *rr,
|
||||
bool verify,
|
||||
@ -391,11 +469,10 @@ static int resource_load_from_web(
|
||||
|
||||
while (left > 0) {
|
||||
_cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
|
||||
_cleanup_(iovec_done) struct iovec h = {};
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
_cleanup_free_ void *h = NULL;
|
||||
Instance *instance;
|
||||
const char *e;
|
||||
size_t hlen;
|
||||
|
||||
/* 64 character hash + separator + filename + newline */
|
||||
if (left < 67)
|
||||
@ -404,7 +481,7 @@ static int resource_load_from_web(
|
||||
if (p[0] == '\\')
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "File names with escapes not supported in manifest at line %zu, refusing.", line_nr);
|
||||
|
||||
r = unhexmem_full(p, 64, /* secure= */ false, &h, &hlen);
|
||||
r = unhexmem_full(p, 64, /* secure= */ false, &h.iov_base, &h.iov_len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse digest at manifest line %zu, refusing.", line_nr);
|
||||
|
||||
@ -433,6 +510,12 @@ static int resource_load_from_web(
|
||||
if (string_has_cc(fn, NULL))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Filename contains control characters at manifest line %zu, refusing.", line_nr);
|
||||
|
||||
r = process_magic_file(fn, &h);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* If this isn't a magic file, then do the pattern matching */
|
||||
|
||||
r = pattern_match_many(rr->patterns, fn, &extracted_fields);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to match pattern: %m");
|
||||
@ -447,16 +530,17 @@ static int resource_load_from_web(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(hlen == sizeof(instance->metadata.sha256sum));
|
||||
assert(h.iov_len == sizeof(instance->metadata.sha256sum));
|
||||
|
||||
if (instance->metadata.sha256sum_set) {
|
||||
if (memcmp(instance->metadata.sha256sum, h, hlen) != 0)
|
||||
if (memcmp(instance->metadata.sha256sum, h.iov_base, h.iov_len) != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "SHA256 sum parsed from filename and manifest don't match at line %zu, refusing.", line_nr);
|
||||
} else {
|
||||
memcpy(instance->metadata.sha256sum, h, hlen);
|
||||
memcpy(instance->metadata.sha256sum, h.iov_base, h.iov_len);
|
||||
instance->metadata.sha256sum_set = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
left -= (e - p) + 1;
|
||||
p = e + 1;
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
#include "copy.h"
|
||||
#include "creds-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
@ -21,6 +20,7 @@
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "image-policy.h"
|
||||
#include "install-file.h"
|
||||
#include "label-util.h"
|
||||
#include "libaudit-util.h"
|
||||
#include "libcrypt-util.h"
|
||||
@ -596,18 +596,6 @@ done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static usec_t epoch_or_now(void) {
|
||||
uint64_t epoch;
|
||||
|
||||
if (secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch) >= 0) {
|
||||
if (epoch > UINT64_MAX/USEC_PER_SEC) /* Overflow check */
|
||||
return USEC_INFINITY;
|
||||
return (usec_t) epoch * USEC_PER_SEC;
|
||||
}
|
||||
|
||||
return now(CLOCK_REALTIME);
|
||||
}
|
||||
|
||||
static int write_temporary_shadow(
|
||||
Context *c,
|
||||
const char *shadow_path,
|
||||
@ -635,7 +623,7 @@ static int write_temporary_shadow(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to open temporary copy of %s: %m", shadow_path);
|
||||
|
||||
lstchg = (long) (epoch_or_now() / USEC_PER_DAY);
|
||||
lstchg = (long) (source_date_epoch_or_now() / USEC_PER_DAY);
|
||||
|
||||
original = fopen(shadow_path, "re");
|
||||
if (original) {
|
||||
|
||||
@ -54,7 +54,11 @@ at_exit() {
|
||||
trap at_exit EXIT
|
||||
|
||||
update_checksums() {
|
||||
(cd "$WORKDIR/source" && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
|
||||
(cd "$WORKDIR/source" && rm -f BEST-BEFORE-* && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
|
||||
}
|
||||
|
||||
update_checksums_with_best_before() {
|
||||
(cd "$WORKDIR/source" && rm -f BEST-BEFORE-* && touch "BEST-BEFORE-$1" && sha256sum uki* part* dir-*.tar.gz "BEST-BEFORE-$1" >SHA256SUMS)
|
||||
}
|
||||
|
||||
new_version() {
|
||||
@ -397,6 +401,21 @@ EOF
|
||||
verify_version "$blockdev" "$sector_size" v6 1
|
||||
verify_version_current "$blockdev" "$sector_size" v7 2
|
||||
|
||||
# Check with a best before in the past
|
||||
update_checksums_with_best_before "$(date -u +'%Y-%m-%d' -d 'last month')"
|
||||
(! "$SYSUPDATE" --verify=no update)
|
||||
|
||||
# Retry but force check off
|
||||
SYSTEMD_SYSUPDATE_VERIFY_FRESHNESS=0 "$SYSUPDATE" --verify=no update
|
||||
|
||||
# Check with best before in the future
|
||||
update_checksums_with_best_before "$(date -u +'%Y-%m-%d' -d 'next month')"
|
||||
"$SYSUPDATE" --verify=no update
|
||||
|
||||
# Check again without a best before
|
||||
update_checksums
|
||||
"$SYSUPDATE" --verify=no update
|
||||
|
||||
# Let's make sure that we don't break our backwards-compat for .conf files
|
||||
# (what .transfer files were called before v257)
|
||||
for i in "$CONFIGDIR/"*.conf; do echo mv "$i" "${i%.conf}.transfer"; done
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user