Skip to content

Commit db6b9d3

Browse files
committed
fixed user password not being set properly
1 parent 30da214 commit db6b9d3

3 files changed

Lines changed: 182 additions & 25 deletions

File tree

Makefile

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: all dynamod installer rootfs iso clean distclean test-qemu test-qemu-serial test-qemu-install
1+
.PHONY: all dynamod installer rootfs iso clean distclean test-qemu test-qemu-serial test-qemu-install test-qemu-disk test-qemu-disk-serial
22

33
ECLIPSE_VERSION ?= 0.1.0
44
BUILD_DIR := $(CURDIR)/build
@@ -96,6 +96,43 @@ test-qemu-install:
9696
-device virtio-vga-gl \
9797
-display gtk,gl=on
9898

99+
test-qemu-disk:
100+
@[ -f $(BUILD_DIR)/test-disk.qcow2 ] || { echo "ERROR: $(BUILD_DIR)/test-disk.qcow2 not found. Run 'make test-qemu-install' and install first."; exit 1; }
101+
@echo "Booting from installed disk..."
102+
@QEMU_EXTRA=""; \
103+
if [ -w /dev/kvm ]; then \
104+
QEMU_EXTRA="-enable-kvm -cpu host"; \
105+
echo "KVM: enabled"; \
106+
else \
107+
echo "KVM: not available (will be slower)"; \
108+
fi; \
109+
qemu-system-x86_64 \
110+
$$QEMU_EXTRA \
111+
-drive file=$(BUILD_DIR)/test-disk.qcow2,format=qcow2,if=virtio \
112+
-m 2048M \
113+
-smp 2 \
114+
-device virtio-vga-gl \
115+
-display gtk,gl=on
116+
117+
test-qemu-disk-serial:
118+
@[ -f $(BUILD_DIR)/test-disk.qcow2 ] || { echo "ERROR: $(BUILD_DIR)/test-disk.qcow2 not found. Run 'make test-qemu-install' and install first."; exit 1; }
119+
@echo "Booting from installed disk (serial console)..."
120+
@echo "Tip: select 'Eclipse Linux (serial console)' from the GRUB menu."
121+
@QEMU_EXTRA=""; \
122+
if [ -w /dev/kvm ]; then \
123+
QEMU_EXTRA="-enable-kvm -cpu host"; \
124+
echo "KVM: enabled"; \
125+
else \
126+
echo "KVM: not available (will be slower)"; \
127+
fi; \
128+
qemu-system-x86_64 \
129+
$$QEMU_EXTRA \
130+
-drive file=$(BUILD_DIR)/test-disk.qcow2,format=qcow2,if=virtio \
131+
-m 2048M \
132+
-smp 2 \
133+
-nographic \
134+
-no-reboot
135+
99136
clean:
100137
rm -rf $(BUILD_DIR)
101138

eclipse-installer/src/install.rs

Lines changed: 101 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -462,15 +462,33 @@ fn setup_accounts(config: &InstallConfig) -> Result<(), String> {
462462
let _ = run_cmd("mount", &["--bind", &src, &dst]);
463463
}
464464

465-
// Set root password
466-
log::log("Setting root password...");
467-
if let Some(ref pass) = config.root_password {
468-
run_chroot_stdin("chpasswd", &[], &format!("root:{}\n", pass))?;
469-
} else {
470-
run_chroot("passwd", &["-d", "root"])?;
465+
// Ensure SHA-512 password hashing — musl's crypt() does not support
466+
// yescrypt ($y$) which newer shadow packages may default to.
467+
let login_defs = format!("{}/etc/login.defs", TARGET_MNT);
468+
if Path::new(&login_defs).exists() {
469+
if let Ok(content) = fs::read_to_string(&login_defs) {
470+
let fixed: String = content
471+
.lines()
472+
.map(|line| {
473+
if line.starts_with("ENCRYPT_METHOD") {
474+
"ENCRYPT_METHOD SHA512"
475+
} else {
476+
line
477+
}
478+
})
479+
.collect::<Vec<_>>()
480+
.join("\n");
481+
let fixed = if content.ends_with('\n') && !fixed.ends_with('\n') {
482+
fixed + "\n"
483+
} else {
484+
fixed
485+
};
486+
let _ = fs::write(&login_defs, &fixed);
487+
log::log("login.defs: ENCRYPT_METHOD set to SHA512");
488+
}
471489
}
472490

473-
// Create user account
491+
// Create user account FIRST so all users exist before chpasswd runs
474492
log::log(&format!("Creating user {}...", config.username));
475493
run_chroot(
476494
"useradd",
@@ -484,17 +502,48 @@ fn setup_accounts(config: &InstallConfig) -> Result<(), String> {
484502
],
485503
)?;
486504

487-
// Set user password
488-
if let Some(ref pass) = config.user_password {
489-
run_chroot_stdin(
490-
"chpasswd",
491-
&[],
492-
&format!("{}:{}\n", config.username, pass),
493-
)?;
494-
} else {
505+
// Handle passwordless accounts
506+
if config.root_password.is_none() {
507+
run_chroot("passwd", &["-d", "root"])?;
508+
}
509+
if config.user_password.is_none() {
495510
run_chroot("passwd", &["-d", &config.username])?;
496511
}
497512

513+
// Set all passwords in a SINGLE chpasswd invocation to avoid
514+
// shadow file locking issues between separate calls.
515+
let mut pw_data = String::new();
516+
if let Some(ref pass) = config.root_password {
517+
pw_data.push_str(&format!("root:{}\n", pass));
518+
}
519+
if let Some(ref pass) = config.user_password {
520+
pw_data.push_str(&format!("{}:{}\n", config.username, pass));
521+
}
522+
523+
if !pw_data.is_empty() {
524+
log::log("Setting passwords via single chpasswd call...");
525+
run_chroot_stdin("chpasswd", &[], &pw_data)?;
526+
527+
// Verify each password was set
528+
for user in &["root", config.username.as_str()] {
529+
if is_shadow_locked(user) {
530+
log::log(&format!("WARNING: chpasswd did not set hash for {user}, trying usermod fallback..."));
531+
let hash = generate_password_hash(user, config)?;
532+
if !hash.is_empty() {
533+
run_chroot("usermod", &["-p", &hash, user])?;
534+
if is_shadow_locked(user) {
535+
return Err(format!("Failed to set password for {user}"));
536+
}
537+
log::log(&format!("OK: password set for {user} via usermod fallback"));
538+
} else {
539+
return Err(format!("Failed to set password for {user} — no hash generation method available"));
540+
}
541+
} else {
542+
log::log(&format!("OK: password set for {user}"));
543+
}
544+
}
545+
}
546+
498547
Ok(())
499548
}
500549

@@ -829,6 +878,43 @@ fn copy_glob_files_with_prefix(search_dir: &str, pattern: &str, staging: &str, t
829878
}
830879
}
831880

881+
/// Check whether a user's shadow entry is still locked (!, !!, *, or empty).
882+
fn is_shadow_locked(user: &str) -> bool {
883+
let shadow_path = format!("{}/etc/shadow", TARGET_MNT);
884+
if let Ok(content) = fs::read_to_string(&shadow_path) {
885+
let prefix = format!("{user}:");
886+
if let Some(line) = content.lines().find(|l| l.starts_with(&prefix)) {
887+
let hash = line.split(':').nth(1).unwrap_or("");
888+
return hash.is_empty()
889+
|| hash == "!"
890+
|| hash == "!!"
891+
|| hash == "*"
892+
|| hash.starts_with("!$");
893+
}
894+
}
895+
true
896+
}
897+
898+
/// Generate a SHA-512 password hash via openssl inside the chroot.
899+
fn generate_password_hash(user: &str, config: &InstallConfig) -> Result<String, String> {
900+
let pass = if user == "root" {
901+
config.root_password.as_deref().unwrap_or("")
902+
} else {
903+
config.user_password.as_deref().unwrap_or("")
904+
};
905+
if pass.is_empty() {
906+
return Ok(String::new());
907+
}
908+
let result = run_chroot_stdin("openssl", &["passwd", "-6", "-stdin"], &format!("{pass}\n"));
909+
if let Ok(output) = result {
910+
let hash = String::from_utf8_lossy(&output.stdout).trim().to_string();
911+
if hash.starts_with("$6$") {
912+
return Ok(hash);
913+
}
914+
}
915+
Err("openssl passwd not available or failed".to_string())
916+
}
917+
832918
fn find_first_existing(candidates: &[&str]) -> Option<String> {
833919
candidates
834920
.iter()

scripts/eclipse-install

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -498,23 +498,57 @@ mount --bind /proc "$TARGET_MNT/proc" 2>/dev/null || true
498498
mount --bind /dev "$TARGET_MNT/dev" 2>/dev/null || true
499499
mount --bind /sys "$TARGET_MNT/sys" 2>/dev/null || true
500500

501-
# Set root password
502-
log "Setting root password..."
503-
if [ -n "$PASS1" ]; then
504-
echo "root:$PASS1" | chroot "$TARGET_MNT" chpasswd >> "$LOG" 2>&1
501+
# Ensure SHA-512 password hashing — musl's crypt() does not support
502+
# yescrypt ($y$) which newer shadow packages may default to.
503+
log "Ensuring SHA-512 password hashing in login.defs..."
504+
if [ -f "$TARGET_MNT/etc/login.defs" ]; then
505+
sed -i 's/^ENCRYPT_METHOD.*/ENCRYPT_METHOD SHA512/' "$TARGET_MNT/etc/login.defs"
506+
log "login.defs ENCRYPT_METHOD set to SHA512"
505507
else
506-
chroot "$TARGET_MNT" passwd -d root >> "$LOG" 2>&1
508+
log "WARNING: /etc/login.defs not found"
507509
fi
508510

509-
# Create user account
511+
# Create user account FIRST so all users exist before chpasswd runs
510512
log "Creating user $USERNAME..."
511513
chroot "$TARGET_MNT" useradd -m -G wheel,audio,video,input,_seatd -s /usr/bin/fish "$USERNAME" >> "$LOG" 2>&1
512-
if [ -n "$USER_PASS1" ]; then
513-
echo "$USERNAME:$USER_PASS1" | chroot "$TARGET_MNT" chpasswd >> "$LOG" 2>&1
514-
else
514+
515+
# Handle passwordless accounts
516+
if [ -z "$PASS1" ]; then
517+
chroot "$TARGET_MNT" passwd -d root >> "$LOG" 2>&1
518+
fi
519+
if [ -z "$USER_PASS1" ]; then
515520
chroot "$TARGET_MNT" passwd -d "$USERNAME" >> "$LOG" 2>&1
516521
fi
517522

523+
# Set all passwords in a SINGLE chpasswd invocation to avoid
524+
# shadow file locking issues between separate calls.
525+
PW_INPUT=""
526+
[ -n "$PASS1" ] && PW_INPUT="root:${PASS1}"
527+
if [ -n "$USER_PASS1" ]; then
528+
[ -n "$PW_INPUT" ] && PW_INPUT="${PW_INPUT}
529+
"
530+
PW_INPUT="${PW_INPUT}${USERNAME}:${USER_PASS1}"
531+
fi
532+
533+
if [ -n "$PW_INPUT" ]; then
534+
log "Setting passwords via single chpasswd call..."
535+
printf '%s\n' "$PW_INPUT" | chroot "$TARGET_MNT" chpasswd >> "$LOG" 2>&1
536+
log "chpasswd exited $?"
537+
538+
# Verify each password was set
539+
for _u in root "$USERNAME"; do
540+
_hash=$(grep "^${_u}:" "$TARGET_MNT/etc/shadow" 2>/dev/null | cut -d: -f2)
541+
case "$_hash" in
542+
'!'*|'*'|'')
543+
log "WARNING: password not set for $_u (field='$_hash')"
544+
;;
545+
*)
546+
log "OK: password set for $_u (${#_hash} chars)"
547+
;;
548+
esac
549+
done
550+
fi
551+
518552
dialog --backtitle "$BACKTITLE" --title "Configuring Bootloader" \
519553
--infobox "Installing GRUB bootloader..." 4 50
520554

0 commit comments

Comments
 (0)