fix: escape sed special chars + reject & | / in passwords
Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes
aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched
pattern. Two layers of defense:
1. validate_pw rejects & | / newline at input
2. sed_escape() helper escapes any remaining special chars before
substitution
This commit is contained in:
parent
46dc615c8e
commit
d0b8678eb5
1 changed files with 31 additions and 13 deletions
|
|
@ -202,17 +202,23 @@ collect_answers() {
|
||||||
# ── Hostname ──
|
# ── Hostname ──
|
||||||
hostname=$(prompt_input "Set hostname" "veilor") || return 1
|
hostname=$(prompt_input "Set hostname" "veilor") || return 1
|
||||||
|
|
||||||
# Reject shell-special chars in passwords. Generated kickstart writes
|
# Reject shell-special and sed-special chars in passwords. Generated
|
||||||
# them via heredoc + sed substitution; bare $, ", \, ` would corrupt
|
# kickstart writes them via heredoc + sed substitution; bare $, ", \, `
|
||||||
# the ks line or partially expand. 8-char min for entropy.
|
# would corrupt the ks line or partially expand at heredoc time.
|
||||||
|
# &, |, /, newline are sed-special: & expands to the matched pattern
|
||||||
|
# (so `aA1!@#%^&*()` becomes `aA1!@#%^__ADMIN_PW__*()`), | is our
|
||||||
|
# delimiter, / would match if delimiter changes, newline breaks the
|
||||||
|
# sed expression. sed_escape() below adds defense-in-depth, but we
|
||||||
|
# also reject these at input so the user sees an immediate error
|
||||||
|
# rather than a corrupted ks file. 8-char min for entropy.
|
||||||
validate_pw() {
|
validate_pw() {
|
||||||
local pw=$1 label=$2
|
local pw=$1 label=$2
|
||||||
if [[ ${#pw} -lt 8 ]]; then
|
if [[ ${#pw} -lt 8 ]]; then
|
||||||
prompt_error "Weak $label — minimum 8 characters."
|
prompt_error "Weak $label — minimum 8 characters."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [[ $pw =~ [\"\$\\\`] ]]; then
|
if [[ $pw =~ [\"\$\\\`\&\|/$'\n'] ]]; then
|
||||||
prompt_error "Invalid $label — cannot contain: \" \$ \\ \`"
|
prompt_error "Invalid $label — cannot contain: \" \$ \\ \` & | / newline"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -253,6 +259,17 @@ Proceed?" || return 1
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# sed_escape — escape sed special chars in a replacement string.
|
||||||
|
# Replacement-side metacharacters: & (matched pattern), \ (escape),
|
||||||
|
# | (our chosen delimiter), / (alternate delimiter — escape too in case
|
||||||
|
# delimiter ever changes). Newline is rejected in validate_pw because
|
||||||
|
# escaping it portably across BSD/GNU sed is fiddly.
|
||||||
|
# Order matters: \ must be escaped FIRST so we don't double-escape the
|
||||||
|
# backslashes we're about to emit for &, |, /.
|
||||||
|
sed_escape() {
|
||||||
|
printf '%s' "$1" | sed -e 's/[\\&|/]/\\&/g'
|
||||||
|
}
|
||||||
|
|
||||||
generate_ks() {
|
generate_ks() {
|
||||||
# Build kickstart for actual disk install.
|
# Build kickstart for actual disk install.
|
||||||
# NOTE: passwords go in via --plaintext to avoid storing crypted hash
|
# NOTE: passwords go in via --plaintext to avoid storing crypted hash
|
||||||
|
|
@ -483,15 +500,16 @@ echo "════════════════════════
|
||||||
# Reboot when done
|
# Reboot when done
|
||||||
reboot
|
reboot
|
||||||
KSEOF
|
KSEOF
|
||||||
# Substitute placeholders. Use | as sed delimiter (passwords might
|
# Substitute placeholders. Use | as sed delimiter. validate_pw()
|
||||||
# contain /). Forbidden chars in passwords (validated upstream): "$\`
|
# already rejects "$\`&|/\n at input — sed_escape() is defence in
|
||||||
# — sed safe.
|
# depth in case future code paths feed unsanitised values (e.g.
|
||||||
|
# locale/hostname from a file, or a relaxed validator).
|
||||||
sed -i \
|
sed -i \
|
||||||
-e "s|__LOCALE__|$SEL_LOCALE|" \
|
-e "s|__LOCALE__|$(sed_escape "$SEL_LOCALE")|" \
|
||||||
-e "s|__HOSTNAME__|$SEL_HOSTNAME|" \
|
-e "s|__HOSTNAME__|$(sed_escape "$SEL_HOSTNAME")|" \
|
||||||
-e "s|__DISK_BASENAME__|$disk_basename|" \
|
-e "s|__DISK_BASENAME__|$(sed_escape "$disk_basename")|" \
|
||||||
-e "s|__LUKS_PW__|$SEL_LUKS_PW|" \
|
-e "s|__LUKS_PW__|$(sed_escape "$SEL_LUKS_PW")|" \
|
||||||
-e "s|__ADMIN_PW__|$SEL_ADMIN_PW|" \
|
-e "s|__ADMIN_PW__|$(sed_escape "$SEL_ADMIN_PW")|" \
|
||||||
"$out"
|
"$out"
|
||||||
echo "[INFO] generated kickstart at $out"
|
echo "[INFO] generated kickstart at $out"
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue