diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 62e064e12bc..e229edc2aac 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -310,6 +310,16 @@ All tools: subvolumes if the backing filesystem supports them. If set to `0`, these lines will always create directories. +`systemd-sysusers` + +* `SOURCE_DATE_EPOCH` — if unset, the field of the date of last password change + in `/etc/shadow` will be the number of days from Jan 1, 1970 00:00 UTC until + today. If SOURCE_DATE_EPOCH is set to a valid UNIX epoch value in seconds, + then the field will be the number of days until that time instead. This is to + support creating bit-by-bit reproducible system images by choosing a + reproducible value for the field of the date of last password change in + `/etc/shadow`. See: https://reproducible-builds.org/specs/source-date-epoch/ + `systemd-sysv-generator`: * `$SYSTEMD_SYSVINIT_PATH` — Controls where `systemd-sysv-generator` looks for diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 62914f15878..55ac11a5124 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -776,6 +776,18 @@ int getenv_bool_secure(const char *p) { return parse_boolean(e); } +int getenv_uint64_secure(const char *p, uint64_t *ret) { + const char *e; + + assert(p); + + e = secure_getenv(p); + if (!e) + return -ENXIO; + + return safe_atou64(e, ret); +} + int set_unset_env(const char *name, const char *value, bool overwrite) { assert(name); diff --git a/src/basic/env-util.h b/src/basic/env-util.h index 2bf0603f212..b927ac7a488 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -57,6 +57,8 @@ char *strv_env_pairs_get(char **l, const char *name) _pure_; int getenv_bool(const char *p); int getenv_bool_secure(const char *p); +int getenv_uint64_secure(const char *p, uint64_t *ret); + /* Like setenv, but calls unsetenv if value == NULL. */ int set_unset_env(const char *name, const char *value, bool overwrite); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 8b2c242e7cf..491e4a0ea28 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -10,6 +10,7 @@ #include "creds-util.h" #include "def.h" #include "dissect-image.h" +#include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" @@ -525,6 +526,18 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char return 0; } +static usec_t epoch_or_now(void) { + uint64_t epoch; + + if (getenv_uint64_secure("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(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) { _cleanup_fclose_ FILE *original = NULL, *shadow = NULL; _cleanup_(unlink_and_freep) char *shadow_tmp = NULL; @@ -545,7 +558,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char if (r < 0) return log_debug_errno(r, "Failed to open temporary copy of %s: %m", shadow_path); - lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + lstchg = (long) (epoch_or_now() / USEC_PER_DAY); original = fopen(shadow_path, "re"); if (original) {