Never truncate the conf file when a saved value breaks the rewrite sed
A value holding a backslash-digit sequence (a backreference to sed) or an embedded line break made _setopt's replace command fail after the shell had already truncated the conf file, wiping the whole domain conf; the next renewal then fails with an empty Le_API and no validation method. Same class as #2426, which escaped only '&' and '|'. Escape the backslash too, write the sed output back only when sed succeeds, reject values holding a line break, and rewrite the file with printf instead of echo in the append path and in _clear_conf: dash's builtin echo interprets backslash escapes and corrupted such values on every rewrite. https://github.com/acmesh-official/acme.sh/issues/7213
This commit is contained in:
@@ -2499,6 +2499,15 @@ _send_signed_request() {
|
||||
|
||||
}
|
||||
|
||||
#Reads a value from stdin, prints it escaped for use as the replacement text
|
||||
#of a sed s command delimited by '|'. The backslash must go first: a bare one
|
||||
#starts an escape sequence and backslash-digit is a backreference, both make
|
||||
#sed error out. Then '&' (the whole-match reference) and the '|' delimiter.
|
||||
#https://github.com/acmesh-official/acme.sh/issues/7213
|
||||
_sed_escape_rhs() {
|
||||
sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/|/\\|/g'
|
||||
}
|
||||
|
||||
#setopt "file" "opt" "=" "value" [";"]
|
||||
_setopt() {
|
||||
__conf="$1"
|
||||
@@ -2514,34 +2523,50 @@ _setopt() {
|
||||
touch "$__conf"
|
||||
chmod 600 "$__conf"
|
||||
fi
|
||||
__nl="
|
||||
"
|
||||
case "$__val" in
|
||||
*"$__nl"*)
|
||||
#the conf format is line based and the file is sourced by the shell, so a
|
||||
#value holding a line break cannot be represented in it (it would also
|
||||
#make the replace sed below fail with an unterminated 's' command)
|
||||
_err "The value of '$__opt' contains a line break, it cannot be saved to $__conf."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
if [ -n "$(_tail_c 1 <"$__conf")" ]; then
|
||||
echo >>"$__conf"
|
||||
fi
|
||||
|
||||
if grep -n "^$__opt$__sep" "$__conf" >/dev/null; then
|
||||
_debug3 OK
|
||||
if _contains "$__val" "&"; then
|
||||
__val="$(echo "$__val" | sed 's/&/\\&/g')"
|
||||
fi
|
||||
if _contains "$__val" "|"; then
|
||||
__val="$(echo "$__val" | sed 's/|/\\|/g')"
|
||||
fi
|
||||
__val="$(printf -- "%s\n" "$__val" | _sed_escape_rhs)"
|
||||
text="$(cat "$__conf")"
|
||||
printf -- "%s\n" "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
|
||||
#capture first, write only on success: redirecting sed straight into the
|
||||
#conf file truncates it before sed runs, so a failing sed (e.g. on an
|
||||
#unescaped special character in the value) wiped the whole conf (#2426)
|
||||
if __text="$(printf -- "%s\n" "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|")"; then
|
||||
printf -- "%s\n" "$__text" >"$__conf"
|
||||
else
|
||||
_err "Cannot save '$__opt' to $__conf."
|
||||
return 1
|
||||
fi
|
||||
|
||||
elif grep -n "^#$__opt$__sep" "$__conf" >/dev/null; then
|
||||
if _contains "$__val" "&"; then
|
||||
__val="$(echo "$__val" | sed 's/&/\\&/g')"
|
||||
fi
|
||||
if _contains "$__val" "|"; then
|
||||
__val="$(echo "$__val" | sed 's/|/\\|/g')"
|
||||
fi
|
||||
__val="$(printf -- "%s\n" "$__val" | _sed_escape_rhs)"
|
||||
text="$(cat "$__conf")"
|
||||
printf -- "%s\n" "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
|
||||
if __text="$(printf -- "%s\n" "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|")"; then
|
||||
printf -- "%s\n" "$__text" >"$__conf"
|
||||
else
|
||||
_err "Cannot save '$__opt' to $__conf."
|
||||
return 1
|
||||
fi
|
||||
|
||||
else
|
||||
_debug3 APP
|
||||
echo "$__opt$__sep$__val$__end" >>"$__conf"
|
||||
#printf, not echo: dash's builtin echo interprets backslash escapes in
|
||||
#the value and would corrupt it
|
||||
printf -- "%s\n" "$__opt$__sep$__val$__end" >>"$__conf"
|
||||
fi
|
||||
_debug3 "$(grep -n "^$__opt$__sep" "$__conf")"
|
||||
}
|
||||
@@ -2569,7 +2594,9 @@ _clear_conf() {
|
||||
_sdkey="$2"
|
||||
if [ "$_c_c_f" ]; then
|
||||
_conf_data="$(cat "$_c_c_f")"
|
||||
echo "$_conf_data" | sed "/^$_sdkey *=.*$/d" >"$_c_c_f"
|
||||
#printf, not echo: dash's builtin echo interprets backslash escapes and
|
||||
#would corrupt saved values that contain them on every rewrite
|
||||
printf -- "%s\n" "$_conf_data" | sed "/^$_sdkey *=.*$/d" >"$_c_c_f"
|
||||
else
|
||||
_err "Config file is empty, cannot clear"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user