Commit a1c7ab47ca for asterisk.org

commit a1c7ab47ca14dc41822bf5f44a5fd5cb563daa68
Author: Mike Bradeen <mbradeen@sangoma.com>
Date:   Thu Jan 15 13:52:30 2026 -0700

    ast_coredumper: check ast_debug_tools.conf permissions

    Prevent ast_coredumper from using ast_debug_tools.conf files that are
    not owned by root or are writable by other users or groups.

    Prevent ast_logescalator and ast_loggrabber from doing the same if
    they are run as root.

    Resolves: #GHSA-rvch-3jmx-3jf3

    UserNote: ast_debug_tools.conf must be owned by root and not be
    writable by other users or groups to be used by ast_coredumper or
    by ast_logescalator or ast_loggrabber when run as root.

diff --git a/contrib/scripts/ast_coredumper b/contrib/scripts/ast_coredumper
index ad752be068..2d8ea76726 100755
--- a/contrib/scripts/ast_coredumper
+++ b/contrib/scripts/ast_coredumper
@@ -42,11 +42,9 @@ COMMANDLINE_COREDUMPS=false

 # Read config files from most important to least important.
 # Variables set on the command line or environment always take precedence.
-# shellcheck disable=SC1091
-[ -f ./ast_debug_tools.conf ] && source ./ast_debug_tools.conf
-# shellcheck disable=SC1090
-[ -f ~/ast_debug_tools.conf ] && source ~/ast_debug_tools.conf
-[ -f /etc/asterisk/ast_debug_tools.conf ] && source /etc/asterisk/ast_debug_tools.conf
+safe_source_config ./ast_debug_tools.conf
+safe_source_config ~/ast_debug_tools.conf
+safe_source_config /etc/asterisk/ast_debug_tools.conf

 if [ -n "${DATEFORMAT}" ] ; then
 	err <<-EOF
@@ -432,6 +430,43 @@ check_gdb() {
 	fi
 }

+# Function to safely source a config file with security checks
+# This prevents privilege escalation by ensuring config files are
+# owned by root and not writable by group or others
+safe_source_config() {
+	local config_file="$1"
+
+	# Return if file doesn't exist
+	[ -f "$config_file" ] || return 0
+
+	# Get the absolute path
+	config_file=$(readlink -f "$config_file")
+
+	# Get file owner UID and permissions
+	local file_stat
+	file_stat=$(stat -c "%u %a" "$config_file" 2>/dev/null) || return 0
+	local owner_uid=${file_stat%% *}
+	local perms=${file_stat##* }
+
+	# File must be owned by root (UID 0)
+	if [ "$owner_uid" -ne 0 ]; then
+		err "Config file $config_file is not owned by root. Skipping for security." >&2
+		return 1
+	fi
+
+	# File must not be writable by group or others (check group-write and other-write bits)
+	# Extract the group and other permission digits
+	local group_perms=$((perms / 10 % 10))
+	local other_perms=$((perms % 10))
+
+	if [ $((group_perms & 2)) -ne 0 ] || [ $((other_perms & 2)) -ne 0 ]; then
+		err "Config file $config_file is writable by group or others. Skipping for security." >&2
+		return 1
+	fi
+
+	source "$config_file"
+}
+
 # shellcheck disable=SC2317
 find_pid() {
 	if [ -n "$PID" ] ; then
diff --git a/contrib/scripts/ast_logescalator b/contrib/scripts/ast_logescalator
index b5a44ccefe..a4eced8eae 100755
--- a/contrib/scripts/ast_logescalator
+++ b/contrib/scripts/ast_logescalator
@@ -127,10 +127,51 @@ declare -A DEBUG_COMMANDS=(
 VERBOSE_LEVELS="NOTICE,WARNING,ERROR,VERBOSE"
 DEBUG_LEVELS="DEBUG"

+# Function to safely source a config file with security checks
+# This prevents privilege escalation by ensuring config files are
+# owned by root and not writable by group or others when running as root
+safe_source_config() {
+	local config_file="$1"
+
+	# Return if file doesn't exist
+	[ -f "$config_file" ] || return 0
+
+	# Get the absolute path
+	config_file=$(readlink -f "$config_file")
+
+	# Check if running as root (effective UID is 0)
+	if [ $EUID -eq 0 ]; then
+		# Running as root - apply strict security checks
+		# Get file owner UID and permissions
+		local file_stat
+		file_stat=$(stat -c "%u %a" "$config_file" 2>/dev/null) || return 0
+		local owner_uid=${file_stat%% *}
+		local perms=${file_stat##* }
+
+		# File must be owned by root (UID 0)
+		if [ "$owner_uid" -ne 0 ]; then
+			echo "WARNING: Config file $config_file is not owned by root. Skipping for security." >&2
+			return 1
+		fi
+
+		# File must not be writable by group or others (check group-write and other-write bits)
+		# Extract the group and other permission digits
+		local group_perms=$((perms / 10 % 10))
+		local other_perms=$((perms % 10))
+
+		if [ $((group_perms & 2)) -ne 0 ] || [ $((other_perms & 2)) -ne 0 ]; then
+			echo "WARNING: Config file $config_file is writable by group or others. Skipping for security." >&2
+			return 1
+		fi
+	fi
+
+	source "$config_file"
+}
+
 # Read config files from least important to most important
-[ -f /etc/asterisk/ast_debug_tools.conf ] && source /etc/asterisk/ast_debug_tools.conf
-[ -f ~/ast_debug_tools.conf ] && source ~/ast_debug_tools.conf
-[ -f ./ast_debug_tools.conf ] && source ./ast_debug_tools.conf
+safe_source_config /etc/asterisk/ast_debug_tools.conf
+safe_source_config ~/ast_debug_tools.conf
+safe_source_config ./ast_debug_tools.conf

 DATEFORMAT=${DATEFORMAT:-'date +%FT%H-%M-%S%z'}
 UNIQUEID=$($DATEFORMAT)
diff --git a/contrib/scripts/ast_loggrabber b/contrib/scripts/ast_loggrabber
index b549ec329a..0683dfd991 100755
--- a/contrib/scripts/ast_loggrabber
+++ b/contrib/scripts/ast_loggrabber
@@ -101,10 +101,51 @@ append_logfiles=false
 declare -a LOGFILES
 declare -a ARGS_LOGFILES

+# Function to safely source a config file with security checks
+# This prevents privilege escalation by ensuring config files are
+# owned by root and not writable by group or others when running as root
+safe_source_config() {
+	local config_file="$1"
+
+	# Return if file doesn't exist
+	[ -f "$config_file" ] || return 0
+
+	# Get the absolute path
+	config_file=$(readlink -f "$config_file")
+
+	# Check if running as root (effective UID is 0)
+	if [ $EUID -eq 0 ]; then
+		# Running as root - apply strict security checks
+		# Get file owner UID and permissions
+		local file_stat
+		file_stat=$(stat -c "%u %a" "$config_file" 2>/dev/null) || return 0
+		local owner_uid=${file_stat%% *}
+		local perms=${file_stat##* }
+
+		# File must be owned by root (UID 0)
+		if [ "$owner_uid" -ne 0 ]; then
+			echo "WARNING: Config file $config_file is not owned by root. Skipping for security." >&2
+			return 1
+		fi
+
+		# File must not be writable by group or others (check group-write and other-write bits)
+		# Extract the group and other permission digits
+		local group_perms=$((perms / 10 % 10))
+		local other_perms=$((perms % 10))
+
+		if [ $((group_perms & 2)) -ne 0 ] || [ $((other_perms & 2)) -ne 0 ]; then
+			echo "WARNING: Config file $config_file is writable by group or others. Skipping for security." >&2
+			return 1
+		fi
+	fi
+
+	source "$config_file"
+}
+
 # Read config files from least important to most important
-[ -f /etc/asterisk/ast_debug_tools.conf ] && source /etc/asterisk/ast_debug_tools.conf
-[ -f ~/ast_debug_tools.conf ] && source ~/ast_debug_tools.conf
-[ -f ./ast_debug_tools.conf ] && source ./ast_debug_tools.conf
+safe_source_config /etc/asterisk/ast_debug_tools.conf
+safe_source_config ~/ast_debug_tools.conf
+safe_source_config ./ast_debug_tools.conf

 if [ ${#LOGFILES[@]} -eq 0 ] ; then
 	LOGFILES+=(/var/log/asterisk/messages* /var/log/asterisk/queue* \
@@ -178,15 +219,14 @@ df=${tarball_uniqueid:-$(${DATEFORMAT})}
 # Extract the Python timestamp conver script from the end of this
 # script and save it to /tmp/.ast_tsconvert.py

-ss=`egrep -n "^#@@@SCRIPTSTART@@@" $0 |cut -f1 -d:`
-tail -n +${ss} $0 >/tmp/.ast_tsconvert.py
+install -m 0600 /dev/stdin /tmp/.ast_tsconvert.py < <(sed '1,/^#@@@SCRIPTSTART@@@/ d' "$0")

 tmpdir=$(mktemp -d)
 if [ -z "$tmpdir" ] ; then
 	echo "${prog}: Unable to create temporary directory."
 	exit 1
 fi
-trap "rm -rf $tmpdir" EXIT
+trap "rm -rf $tmpdir /tmp/.ast_tsconvert.py" EXIT
 tardir=asterisk-${df}.logfiles

 # Now iterate over the logfiles