Admin password check works with yescrypt; shorter Blancco version warning

chpasswd and passwd write yescrypt ($y$) hashes, which the openssl $6$
comparison could not read: every re-run set the pxe password again, and a
box whose password was 'pxe' in yescrypt passed the pxe_admin_password
requirement. Both Admin tasks now compare with crypt(), which handles every
scheme. The config.img warning names the two Blancco versions instead of
the staging guard's merge instructions.
This commit is contained in:
cproudlock
2026-10-17 12:57:25 -04:00
parent c2b0c3d35e
commit 3a8b7cabc7
3 changed files with 38 additions and 26 deletions

View File

@@ -400,16 +400,24 @@
the Blancco preferences). Fix them in playbook/site-vars.yml and re-run.
quiet: yes
# MATCH / DIFF / UNKNOWN: does pxe's shadow hash match $PW? crypt()
# handles every scheme (the autoinstall hash is $6$, chpasswd and passwd
# write yescrypt $y$). Used by the two Admin tasks below.
- name: "Admin: is the Linux pxe login still the shipped password?"
shell: |
h=$(getent shadow pxe | cut -d: -f2)
case "$h" in
'$6$'*) salt=$(printf '%s' "$h" | cut -d'$' -f3)
if [ "$(printf 'pxe' | openssl passwd -6 -salt "$salt" -stdin)" = "$h" ]; then echo DEFAULT; else echo SITE; fi ;;
*) echo UNKNOWN ;;
esac
args:
executable: /bin/bash
shell: &pxe_pw_compare |
python3 -W ignore - <<'PY'
import os, subprocess
try:
import crypt
except ImportError:
print("UNKNOWN"); raise SystemExit
h = subprocess.run(["getent", "shadow", "pxe"], capture_output=True, text=True).stdout.split(":")
h = h[1] if len(h) > 1 else ""
print("MATCH" if h.startswith("$") and crypt.crypt(os.environ["PW"], h) == h else
"DIFF" if h.startswith("$") else "UNKNOWN")
PY
environment:
PW: "pxe"
register: pxe_admin_state
changed_when: false
@@ -417,7 +425,7 @@
assert:
that:
- pxe_admin_password is string
- pxe_admin_password | length > 0 or pxe_admin_state.stdout != 'DEFAULT'
- pxe_admin_password | length > 0 or pxe_admin_state.stdout != 'MATCH'
- pxe_admin_password | length == 0 or (pxe_admin_password | length >= 8 and pxe_admin_password | length <= 128)
- pxe_admin_password | length == 0 or pxe_admin_password is not search('[\x00-\x1f]')
- pxe_admin_password != 'pxe'
@@ -1551,24 +1559,22 @@
changed_when: "'created' in smb_users.stdout"
no_log: true
- name: "Admin: set the Linux pxe password from pxe_admin_password"
shell: |
h=$(getent shadow pxe | cut -d: -f2)
salt=$(printf '%s' "$h" | cut -d'$' -f3)
if [ -n "$salt" ] && [ "$(printf '%s' "$PW" | openssl passwd -6 -salt "$salt" -stdin)" = "$h" ]; then
echo UNCHANGED
else
printf 'pxe:%s\n' "$PW" | chpasswd && echo CHANGED
fi
- name: "Admin: does the Linux pxe password already match pxe_admin_password?"
shell: *pxe_pw_compare
environment:
PW: "{{ pxe_admin_password }}"
args:
executable: /bin/bash
register: pxe_admin_set
changed_when: "'CHANGED' in pxe_admin_set.stdout"
register: pxe_admin_cur
changed_when: false
when: pxe_admin_password | length > 0
no_log: true
- name: "Admin: set the Linux pxe password from pxe_admin_password"
shell: printf 'pxe:%s\n' "$PW" | chpasswd
environment:
PW: "{{ pxe_admin_password }}"
when: pxe_admin_password | length > 0 and pxe_admin_cur.stdout != 'MATCH'
no_log: true
- name: "Deploy nf_conntrack TCP timeout sysctl for PXE workload"
copy:
src: "{{ usb_mount }}/pxe-server-helpers/99-pxe-conntrack.conf"

View File

@@ -1011,10 +1011,15 @@ def refresh_live_prefs():
ours = f.read()
try:
new = patch_config_img(cfg, ours)
except (StageBlocked, ValueError) as e:
warning = (f"the live config.img (BIOS boot) was not updated: {e} "
except StageBlocked:
live = prefs_version(cpio_member(cfg, "preferences.xml") or b"") or "?"
warning = (f"the live config.img (BIOS boot) is Blancco {live}, older than our "
f"{prefs_version(ours) or '?'} preferences, so it was not updated. "
"Stage a current release on Updates > Blancco releases.")
new = cfg
except ValueError as e:
warning = f"the live config.img (BIOS boot) was not updated: {e}"
new = cfg
if new != cfg:
_atomic_write(cfg_path, new)
changed = True

View File

@@ -190,7 +190,8 @@ class ShareAccounts(Fixture):
with open(os.path.join(BU.blancco_root(), "config.img"), "wb") as f:
f.write(cfg)
done = SA.set_password("blancco", PW)
self.assertTrue(any("WARNING" in d and "config.img" in d for d in done), done)
self.assertTrue(any("WARNING" in d and "config.img (BIOS boot) is Blancco 7.15" in d
and "older than our 7.20" in d for d in done), done)
with open(BP.served_prefs_path(), "rb") as f:
self.assertEqual(self.prefs_share_pw(f.read()), PW)
with open(os.path.join(BU.blancco_root(), "config.img"), "rb") as f: