244 lines
9.4 KiB
Bash
244 lines
9.4 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# Direct-exec root helper for one staged A1 transaction.
|
|
|
|
_k3slra1h_command() {
|
|
"$@"
|
|
}
|
|
|
|
_k3slra1h_euid_is_root() {
|
|
(( EUID == 0 ))
|
|
}
|
|
|
|
_k3slra1h_output_name_is_safe() {
|
|
local output_name="${1-}"
|
|
(( $# == 1 )) || return 1
|
|
[[ "$output_name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || return 1
|
|
[[ "$output_name" != _k3slra1h_* ]]
|
|
}
|
|
|
|
_k3slra1h_capture_metadata() {
|
|
local target="${1-}" output_name="${2-}" line=''
|
|
(( $# == 2 )) || return 1
|
|
_k3slra1h_output_name_is_safe "$output_name" || return 1
|
|
case "$output_name" in target|output_name|line|record) return 1 ;; esac
|
|
_k3slra1h_packet_line metadata "$target" line || return 1
|
|
printf -v "$output_name" '%s' "$line"
|
|
}
|
|
|
|
_k3slra1h_capture_canonical() {
|
|
local target="${1-}" output_name="${2-}" line=''
|
|
(( $# == 2 )) || return 1
|
|
_k3slra1h_output_name_is_safe "$output_name" || return 1
|
|
case "$output_name" in target|output_name|line) return 1 ;; esac
|
|
_k3slra1h_packet_line readlink "$target" line || return 1
|
|
[[ "$line" == "$target" ]] || return 1
|
|
printf -v "$output_name" '%s' "$line"
|
|
}
|
|
|
|
_k3slra1h_stderr_marker() {
|
|
local chunk='' read_rc=0
|
|
if IFS= read -r -d '' -n 1 chunk; then
|
|
read_rc=0
|
|
else
|
|
read_rc=$?
|
|
fi
|
|
if (( read_rc == 0 )); then
|
|
printf '\0STDERR\0'
|
|
while IFS= read -r -d '' -n 4096 chunk; do
|
|
:
|
|
done
|
|
else
|
|
if [[ -n "$chunk" ]]; then
|
|
printf '\0STDERR\0'
|
|
fi
|
|
fi
|
|
}
|
|
|
|
_k3slra1h_metadata_packet() {
|
|
local target="${1-}" rc=0
|
|
(( $# == 1 )) || return 1
|
|
if _k3slra1h_command /usr/bin/stat --format='%d:%i|%h|%s|%y|%F|%u:%g|%a' -- "$target" 2> >(_k3slra1h_stderr_marker); then
|
|
rc=0
|
|
else
|
|
rc=$?
|
|
fi
|
|
printf '\0RC=%s\0' "$rc"
|
|
}
|
|
|
|
_k3slra1h_readlink_packet() {
|
|
local target="${1-}" rc=0
|
|
(( $# == 1 )) || return 1
|
|
if _k3slra1h_command /usr/bin/readlink --canonicalize-existing -- "$target" 2> >(_k3slra1h_stderr_marker); then
|
|
rc=0
|
|
else
|
|
rc=$?
|
|
fi
|
|
printf '\0RC=%s\0' "$rc"
|
|
}
|
|
|
|
_k3slra1h_packet_value() {
|
|
local producer="${1-}" argument="${2-}" output_name="${3-}"
|
|
local -a records=()
|
|
(( $# == 3 )) || return 1
|
|
_k3slra1h_output_name_is_safe "$output_name" || return 1
|
|
case "$output_name" in producer|argument|output_name|records) return 1 ;; esac
|
|
case "$producer" in
|
|
metadata) mapfile -d '' -t records < <(_k3slra1h_metadata_packet "$argument") ;;
|
|
readlink) mapfile -d '' -t records < <(_k3slra1h_readlink_packet "$argument") ;;
|
|
*) return 1 ;;
|
|
esac
|
|
(( ${#records[@]} == 2 )) || return 1
|
|
[[ "${records[1]}" == RC=0 ]] || return 1
|
|
printf -v "$output_name" '%s' "${records[0]}"
|
|
}
|
|
|
|
_k3slra1h_packet_line() {
|
|
local producer="${1-}" argument="${2-}" output_name="${3-}" value=''
|
|
(( $# == 3 )) || return 1
|
|
_k3slra1h_output_name_is_safe "$output_name" || return 1
|
|
case "$output_name" in producer|argument|output_name|value) return 1 ;; esac
|
|
_k3slra1h_packet_value "$producer" "$argument" value || return 1
|
|
[[ "$value" == *$'\n' ]] || return 1
|
|
[[ "$value" != *$'\r'* ]] || return 1
|
|
value="${value%$'\n'}"
|
|
[[ -n "$value" ]] || return 1
|
|
[[ "$value" != *$'\n'* ]] || return 1
|
|
printf -v "$output_name" '%s' "$value"
|
|
}
|
|
|
|
_k3slra1h_output_names_are_distinct() {
|
|
local name
|
|
local -A seen=()
|
|
(( $# >= 1 )) || return 1
|
|
for name in "$@"; do
|
|
_k3slra1h_output_name_is_safe "$name" || return 1
|
|
[[ "${seen[$name]+set}" != set ]] || return 1
|
|
printf -v 'seen[$name]' '%s' 1
|
|
done
|
|
}
|
|
|
|
_k3slra1h_metadata_fields() {
|
|
local target="${1-}" out_identity="${2-}" out_nlink="${3-}" out_size="${4-}"
|
|
local out_mtime="${5-}" out_type="${6-}" out_owner="${7-}" out_mode="${8-}"
|
|
local metadata='' normalized_type=''
|
|
(( $# == 8 )) || return 1
|
|
_k3slra1h_output_names_are_distinct "$out_identity" "$out_nlink" "$out_size" "$out_mtime" "$out_type" "$out_owner" "$out_mode" || return 1
|
|
case "$out_identity|$out_nlink|$out_size|$out_mtime|$out_type|$out_owner|$out_mode" in
|
|
*'|target|'*|target\|*|*\|target|*'|out_identity|'*|out_identity\|*|*\|out_identity|*'|out_nlink|'*|out_nlink\|*|*\|out_nlink|*'|out_size|'*|out_size\|*|*\|out_size|*'|out_mtime|'*|out_mtime\|*|*\|out_mtime|*'|out_type|'*|out_type\|*|*\|out_type|*'|out_owner|'*|out_owner\|*|*\|out_owner|*'|out_mode|'*|out_mode\|*|*\|out_mode|*'|metadata|'*|metadata\|*|*\|metadata|*'|normalized_type|'*|normalized_type\|*|*\|normalized_type) return 1 ;;
|
|
esac
|
|
_k3slra1h_capture_metadata "$target" metadata || return 1
|
|
[[ "$metadata" =~ ^([0-9]+:[0-9]+)\|([0-9]+)\|([0-9]+)\|([^|]+)\|(directory|regular[[:space:]]file|regular[[:space:]]empty[[:space:]]file|symbolic[[:space:]]link)\|([0-9]+:[0-9]+)\|([0-9]+)$ ]] || return 1
|
|
normalized_type="${BASH_REMATCH[5]}"
|
|
if [[ "$normalized_type" == 'regular empty file' ]]; then
|
|
printf -v normalized_type '%s' 'regular file'
|
|
fi
|
|
printf -v "$out_identity" '%s' "${BASH_REMATCH[1]}"
|
|
printf -v "$out_nlink" '%s' "${BASH_REMATCH[2]}"
|
|
printf -v "$out_size" '%s' "${BASH_REMATCH[3]}"
|
|
printf -v "$out_mtime" '%s' "${BASH_REMATCH[4]}"
|
|
printf -v "$out_type" '%s' "$normalized_type"
|
|
printf -v "$out_owner" '%s' "${BASH_REMATCH[6]}"
|
|
printf -v "$out_mode" '%s' "${BASH_REMATCH[7]}"
|
|
}
|
|
|
|
_k3slra1h_regular_root_file_is_safe() {
|
|
local target="${1-}" expected_device="${2-}" canonical=''
|
|
local identity='' nlink='' size='' mtime='' type='' owner='' mode=''
|
|
(( $# == 2 )) || return 1
|
|
_k3slra1h_capture_canonical "$target" canonical || return 1
|
|
[[ "$canonical" == "$target" ]] || return 1
|
|
_k3slra1h_metadata_fields "$target" identity nlink size mtime type owner mode || return 1
|
|
[[ "${identity%%:*}" == "$expected_device" ]] || return 1
|
|
[[ "$nlink" == 1 ]] || return 1
|
|
[[ "$type" == 'regular file' ]] || return 1
|
|
[[ "$owner" == 0:0 ]] || return 1
|
|
[[ "$mode" == 600 ]]
|
|
}
|
|
|
|
_k3slra1h_root_directory_is_safe() {
|
|
local target="${1-}" expected_device="${2-}" canonical=''
|
|
local identity='' nlink='' size='' mtime='' type='' owner='' mode=''
|
|
(( $# == 2 )) || return 1
|
|
_k3slra1h_capture_canonical "$target" canonical || return 1
|
|
[[ "$canonical" == "$target" ]] || return 1
|
|
_k3slra1h_metadata_fields "$target" identity nlink size mtime type owner mode || return 1
|
|
[[ "${identity%%:*}" == "$expected_device" ]] || return 1
|
|
[[ "$type" == directory ]] || return 1
|
|
[[ "$owner" == 0:0 ]] || return 1
|
|
[[ "$mode" == 700 ]]
|
|
}
|
|
|
|
_k3slra1h_staged_root_is_safe() {
|
|
local capture_root="${1-}" canonical='' device=''
|
|
local identity='' nlink='' size='' mtime='' type='' owner='' mode=''
|
|
(( $# == 1 )) || return 1
|
|
[[ "$capture_root" == /* ]] || return 1
|
|
[[ "$capture_root" != */ ]] || return 1
|
|
[[ "$capture_root" != *//* ]] || return 1
|
|
_k3slra1h_capture_canonical "$capture_root" canonical || return 1
|
|
[[ "$canonical" == "$capture_root" ]] || return 1
|
|
_k3slra1h_metadata_fields "$capture_root" identity nlink size mtime type owner mode || return 1
|
|
[[ "$type" == directory ]] || return 1
|
|
[[ "$owner" == 0:0 ]] || return 1
|
|
[[ "$mode" == 700 ]] || return 1
|
|
device="${identity%%:*}"
|
|
[[ "$device" =~ ^[0-9]+$ ]] || return 1
|
|
_k3slra1h_root_directory_is_safe "${capture_root}/lib" "$device" || return 1
|
|
_k3slra1h_root_directory_is_safe "${capture_root}/libexec" "$device" || return 1
|
|
_k3slra1h_root_directory_is_safe "${capture_root}/contract" "$device" || return 1
|
|
_k3slra1h_regular_root_file_is_safe "${capture_root}/lib/k3s-local-recovery.sh" "$device" || return 1
|
|
_k3slra1h_regular_root_file_is_safe "${capture_root}/lib/k3s-local-recovery-a1.sh" "$device" || return 1
|
|
_k3slra1h_regular_root_file_is_safe "${capture_root}/contract/local-recovery.env" "$device" || return 1
|
|
_k3slra1h_regular_root_file_is_safe "${capture_root}/libexec/k3s-local-recovery-a1-check.sh" "$device"
|
|
}
|
|
|
|
_k3slra1h_load_staged_dependencies() {
|
|
local capture_root="${1-}" shared_library='' a1_library='' contract_file=''
|
|
(( $# == 1 )) || return 1
|
|
_k3slra1h_staged_root_is_safe "$capture_root" || return 1
|
|
shared_library="${capture_root}/lib/k3s-local-recovery.sh"
|
|
a1_library="${capture_root}/lib/k3s-local-recovery-a1.sh"
|
|
contract_file="${capture_root}/contract/local-recovery.env"
|
|
source "$shared_library" || return 1
|
|
source "$a1_library" || return 1
|
|
_k3slr_load_contract "$contract_file" || return 1
|
|
[[ "${K3SLR_MAPPING_NAME-}" == "$K3SLRA1_MAIN_MAPPING_NAME" ]] || return 1
|
|
[[ "${K3SLR_PROOF_MAPPING_NAME-}" == k3s-recovery-proof ]]
|
|
}
|
|
|
|
_k3slra1h_check_main_impl() {
|
|
local mount_root="${1-}" role="${2-}" capture_root="${3-}"
|
|
(( $# == 3 )) || return 1
|
|
_k3slra1h_euid_is_root || return 1
|
|
[[ "$-" != *x* ]] || return 1
|
|
case "$role" in
|
|
original-baseline) [[ "$mount_root" == "${capture_root}/original-baseline-mount" ]] || return 1 ;;
|
|
proof) [[ "$mount_root" == "${capture_root}/proof-mount" ]] || return 1 ;;
|
|
original-final) [[ "$mount_root" == "${capture_root}/original-final-mount" ]] || return 1 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
_k3slra1h_load_staged_dependencies "$capture_root" || return 1
|
|
_k3slra1_verify_payload_root "$mount_root" "$role" "$capture_root"
|
|
}
|
|
|
|
k3slra1_check_main() {
|
|
_k3slra1h_check_main_impl "$@" >/dev/null 2>&1
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
_k3slra1h_direct_signal_rc=0
|
|
_k3slra1h_direct_rc=0
|
|
trap '_k3slra1h_direct_signal_rc=130; if (( _k3slra1h_direct_rc == 0 )); then _k3slra1h_direct_rc=130; fi' INT
|
|
trap '_k3slra1h_direct_signal_rc=143; if (( _k3slra1h_direct_rc == 0 )); then _k3slra1h_direct_rc=143; fi' TERM
|
|
if k3slra1_check_main "$@"; then
|
|
_k3slra1h_direct_rc=0
|
|
else
|
|
_k3slra1h_direct_rc=$?
|
|
fi
|
|
if (( _k3slra1h_direct_signal_rc != 0 && _k3slra1h_direct_rc == 0 )); then
|
|
_k3slra1h_direct_rc=$_k3slra1h_direct_signal_rc
|
|
fi
|
|
exit "$_k3slra1h_direct_rc"
|
|
fi
|