]> granicus.if.org Git - shadow/commitdiff
Make the sp_lstchg shadow field reproducible.
authorChris Lamb <lamby@debian.org>
Wed, 15 Mar 2017 10:36:21 +0000 (10:36 +0000)
committerChris Lamb <chris@chris-lamb.co.uk>
Mon, 10 Apr 2017 21:29:21 +0000 (22:29 +0100)
The third field in the /etc/shadow file (sp_lstchg) contains the date of
the last password change expressed as the number of days since Jan 1, 1970.
As this is a relative time, creating a user today will result in:

   username:17238:0:99999:7:::

whilst creating the same user tomorrow will result in:

    username:17239:0:99999:7:::

This has an impact for the Reproducible Builds[0] project where we aim to
be independent of as many elements the build environment as possible,
including the current date.

This patch changes the behaviour to use the SOURCE_DATE_EPOCH[1]
environment variable (instead of Jan 1, 1970) if valid.

 [0] https://reproducible-builds.org/
 [1] https://reproducible-builds.org/specs/source-date-epoch/

Signed-off-by: Chris Lamb <lamby@debian.org>
lib/prototypes.h
libmisc/Makefile.am
libmisc/gettime.c [new file with mode: 0644]
src/chpasswd.c
src/newusers.c
src/passwd.c
src/useradd.c
src/usermod.c

index 7aaf1a633d8a2ec5762f4e5608581544c4a74f45..4808d5d985fae0aa07e7ec84f829c675462ea1c5 100644 (file)
@@ -179,6 +179,9 @@ extern int getrange (char *range,
                      unsigned long *min, bool *has_min,
                      unsigned long *max, bool *has_max);
 
+/* gettime.c */
+extern time_t gettime ();
+
 /* get_uid.c */
 extern int get_uid (const char *uidstr, uid_t *uid);
 
index e0b6d8ca4907c04c4bc0c19a75be3f5018e91294..4a62049566e49b7c1a3ac9aba8f5b97c86425abc 100644 (file)
@@ -31,6 +31,7 @@ libmisc_a_SOURCES = \
        getdate.y \
        getgr_nam_gid.c \
        getrange.c \
+       gettime.c \
        hushed.c \
        idmapping.h \
        idmapping.c \
diff --git a/libmisc/gettime.c b/libmisc/gettime.c
new file mode 100644 (file)
index 0000000..53eaf51
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017, Chris Lamb
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the copyright holders or contributors may not be used to
+ *    endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include "defines.h"
+#include "prototypes.h"
+
+/*
+ * gettime() returns the time as the number of seconds since the Epoch
+ *
+ * Like time(), gettime() returns the time as the number of seconds since the
+ * Epoch, 1970-01-01 00:00:00 +0000 (UTC), except that if the SOURCE_DATE_EPOCH
+ * environment variable is exported it will use that instead.
+ */
+/*@observer@*/time_t gettime ()
+{
+       char *endptr;
+       char *source_date_epoch;
+       time_t fallback;
+       unsigned long long epoch;
+
+       fallback = time (NULL);
+       source_date_epoch = getenv ("SOURCE_DATE_EPOCH");
+
+       if (!source_date_epoch)
+               return fallback;
+
+       errno = 0;
+       epoch = strtoull (source_date_epoch, &endptr, 10);
+       if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0))
+                       || (errno != 0 && epoch == 0)) {
+               fprintf (stderr,
+                        _("Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n"),
+                        strerror(errno));
+       } else if (endptr == source_date_epoch) {
+               fprintf (stderr,
+                        _("Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n"),
+                        endptr);
+       } else if (*endptr != '\0') {
+               fprintf (stderr,
+                        _("Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n"),
+                        endptr);
+       } else if (epoch > ULONG_MAX) {
+               fprintf (stderr,
+                        _("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to %lu but was found to be: %llu\n"),
+                        ULONG_MAX, epoch);
+       } else if (epoch > fallback) {
+               fprintf (stderr,
+                        _("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to the current time (%lu) but was found to be: %llu\n"),
+                        fallback, epoch);
+       } else {
+               /* Valid */
+               return (time_t)epoch;
+       }
+
+       return fallback;
+}
index 21008f58cdf2bd68604dfdd88eae0e607d799429..f9856726de0de7c6817baa09372a9dc601f87e9e 100644 (file)
@@ -552,7 +552,7 @@ int main (int argc, char **argv)
                if (NULL != sp) {
                        newsp = *sp;
                        newsp.sp_pwdp = cp;
-                       newsp.sp_lstchg = (long) time ((time_t *)NULL) / SCALE;
+                       newsp.sp_lstchg = (long) gettime () / SCALE;
                        if (0 == newsp.sp_lstchg) {
                                /* Better disable aging than requiring a
                                 * password change */
index 0c0cfe40e3a845d3e12f800260bb13385ed889e8..c38aec4b43b85163a5b9ae832d4ce0c6e41cf69e 100644 (file)
@@ -496,7 +496,7 @@ static int add_passwd (struct passwd *pwd, const char *password)
                        }
                        spent.sp_pwdp = cp;
                }
-               spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+               spent.sp_lstchg = (long) gettime () / SCALE;
                if (0 == spent.sp_lstchg) {
                        /* Better disable aging than requiring a password
                         * change */
@@ -553,7 +553,7 @@ static int add_passwd (struct passwd *pwd, const char *password)
         */
        spent.sp_pwdp = "!";
 #endif
-       spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+       spent.sp_lstchg = (long) gettime () / SCALE;
        if (0 == spent.sp_lstchg) {
                /* Better disable aging than requiring a password change */
                spent.sp_lstchg = -1;
index 1191111d0d8a5600ab9b93c7769a02fc1f2a65bb..3af3e65173231aa52c506653375bdae9eef7021c 100644 (file)
@@ -668,7 +668,7 @@ static void update_shadow (void)
        }
 #ifndef USE_PAM
        if (do_update_age) {
-               nsp->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+               nsp->sp_lstchg = (long) gettime () / SCALE;
                if (0 == nsp->sp_lstchg) {
                        /* Better disable aging than requiring a password
                         * change */
index 6d944056a5718b8c3e40390e0c89005e0a8c78dd..0e0fa1f8e8fee3eded20c701fb340673003e32e6 100644 (file)
@@ -828,7 +828,7 @@ static void new_spent (struct spwd *spent)
        memzero (spent, sizeof *spent);
        spent->sp_namp = (char *) user_name;
        spent->sp_pwdp = (char *) user_pass;
-       spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+       spent->sp_lstchg = (long) gettime () / SCALE;
        if (0 == spent->sp_lstchg) {
                /* Better disable aging than requiring a password change */
                spent->sp_lstchg = -1;
index 68e31e4f74337fb2e05d7251d575d03c13b6db09..9c5e479fe877199ca79944ffb3e5555c3371a20e 100644 (file)
@@ -641,7 +641,7 @@ static void new_spent (struct spwd *spent)
        spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp);
 
        if (pflg) {
-               spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+               spent->sp_lstchg = (long) gettime () / SCALE;
                if (0 == spent->sp_lstchg) {
                        /* Better disable aging than requiring a password
                         * change. */
@@ -1673,7 +1673,7 @@ static void usr_update (void)
                        spent.sp_pwdp   = xstrdup (pwent.pw_passwd);
                        pwent.pw_passwd = xstrdup (SHADOW_PASSWD_STRING);
 
-                       spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+                       spent.sp_lstchg = (long) gettime () / SCALE;
                        if (0 == spent.sp_lstchg) {
                                /* Better disable aging than
                                 * requiring a password change */