Skip to content

Commit

Permalink
compose: remove lastchg values from [/usr]/etc/shadow
Browse files Browse the repository at this point in the history
The format of /etc/shadow contains several fields relating to
password aging. One of these, `lastchg`, contains a timestamp
that represents the epoch day of the last password change. Tools
that don't respect SOURCE_DATE_EPOCH, which includes systemd's
`sysusers` tool, will set this to a value based on the current
time of the build. This causes a lack of reproducibility.

The `lastchg` field can be safely made empty. This disables the
password aging features.

This change rewrites /etc/shadow to remove all `lastchg` values.

This removal could be made conditional on the entry containing
an encrypted password field indicating that the account is either
locked or otherwise restricted from using passwords, such as `*`
or anything starting with `!`.
  • Loading branch information
jeamland committed Oct 10, 2021
1 parent a7ab6b2 commit 8f1a2b4
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 1 deletion.
24 changes: 23 additions & 1 deletion rust/src/normalization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use lazy_static::lazy_static;
use ostree_ext::gio;
use std::convert::TryInto;
use std::ffi::{OsStr, OsString};
use std::io::{Read, Seek, Write};
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
use std::path::Path;
use subprocess::{Exec, Redirection};

lazy_static! {
static ref SOURCE_DATE_EPOCH_RAW: Option<OsString> = std::env::var_os("SOURCE_DATE_EPOCH");
Expand Down Expand Up @@ -129,6 +131,26 @@ pub(crate) fn normalize_rpmdb<P: AsRef<Path>>(
}
}

#[context("Rewriting /etc/shadow to remove lastchg field")]
pub(crate) fn normalize_etc_shadow(rootfs: &openat::Dir) -> Result<()> {
let mut shadow = rootfs.update_file("usr/etc/shadow", 0o400)?;
let entries = BufReader::new(&mut shadow)
.lines()
.map(|l| {
let l = l?;
Ok(l.split(':').map(|s| s.to_string()).collect())
})
.collect::<std::io::Result<Vec<Vec<String>>>>()?;
shadow.seek(SeekFrom::Start(0))?;
shadow.set_len(0)?;
for mut entry in entries {
entry[2] = String::new();
writeln!(shadow, "{}", entry.join(":"))?;
}

Ok(())
}

mod rpmdb_bdb {
use super::SOURCE_DATE_EPOCH;
use anyhow::{anyhow, Context, Result};
Expand Down
6 changes: 6 additions & 0 deletions rust/src/passwd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use crate::cxxrsutil::*;
use crate::ffiutil;
use crate::nameservice;
use crate::normalization;
use crate::treefile::{CheckGroups, CheckPasswd, Treefile};
use anyhow::{anyhow, Context, Result};
use fn_error_context::context;
Expand Down Expand Up @@ -518,6 +519,11 @@ fn complete_pwgrp(rootfs: &openat::Dir) -> Result<()> {
// In actuality, nothing should change /etc/shadow or /etc/gshadow, so
// we'll just have to pay the (tiny) cost of re-checksumming.

// /etc/shadow ends up with a timestamp in it thanks to the `lastchg`
// field. This can be made empty safely, especially for accounts that
// are locked.
normalization::normalize_etc_shadow(rootfs)?;

Ok(())
}

Expand Down

0 comments on commit 8f1a2b4

Please sign in to comment.