mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 10:07:12 +00:00
Unconfined delegates access to open file descriptors. Therefore when running a confined binary from unconfined, it will work even when the attachment path is not read-allowed. However, as soon as these confined binaries are run from another confined process, this delegation is not permitted anymore and the program breaks. This has been the cause of several bugs such as https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/2107455 or https://github.com/canonical/snapd/pull/15181 . Introduce `test_profile.sh`, a helper script that ensures confining AppArmor profiles explicitly allow (at least) read access to their attachment path. Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
85 lines
2.3 KiB
Bash
Executable File
85 lines
2.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Check if the current profile allow reading its attachment
|
|
check_entry() {
|
|
local prof_name="$1"
|
|
local -n lines_ref="$2"
|
|
local attachment="$3"
|
|
local found=0
|
|
|
|
for line in "${lines_ref[@]}"; do
|
|
if [[ $line == Perms:*r*:*"($attachment)"* ]]; then
|
|
found=1
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ $found -eq 0 ]]; then
|
|
echo -e "\e[0;31mProfile $prof_name: ERROR: no Perms rule for '$attachment'.\e[0m"
|
|
exit 1
|
|
fi
|
|
|
|
[[ -n "${VERBOSE:-}" ]] && echo -e "\e[0;32mProfile $prof_name: OK '$attachment' found\e[0m" || true
|
|
}
|
|
|
|
# Handle the end of a profile block: either skip it or check for the entry.
|
|
finish_profile() {
|
|
local name="$1"
|
|
local prof_file="$2"
|
|
local skip="$3"
|
|
local attachment="$4"
|
|
local arr_name="$5"
|
|
|
|
if [[ -n $name ]]; then
|
|
if [[ $skip != 0 ]]; then
|
|
[[ -n "${VERBOSE:-}" ]] && echo "Profile '$name' skipped: $skip" || true
|
|
else
|
|
check_entry "$prof_file ($name)" "$arr_name" "$attachment"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
process_profile() {
|
|
local prof_file="$1"
|
|
shift
|
|
local dump curr_name="" attachment="" skip_profile=0 in_entries=0
|
|
local block_lines=()
|
|
|
|
if ! dump=$(../parser/apparmor_parser $@ -d "$prof_file" 2>&1); then
|
|
echo "\e[0;31mERROR: Failed to parse '$prof_file': $dump\e[0m" >&2
|
|
exit 1
|
|
fi
|
|
|
|
IFS=$'\n' read -r -d '' -a lines < <(printf '%s\n' "$dump" && printf '\0')
|
|
|
|
for line in "${lines[@]}"; do
|
|
if [[ $line =~ ^[[:space:]]*Name:[[:space:]]*([^[:space:]]+) ]]; then
|
|
finish_profile "$curr_name" "$prof_file" "$skip_profile" "$attachment" block_lines
|
|
curr_name="${BASH_REMATCH[1]}"
|
|
attachment="" skip_profile=0 in_entries=0 block_lines=()
|
|
elif [[ $line =~ ^[[:space:]]*Mode:[[:space:]]*unconfined ]]; then
|
|
skip_profile="unconfined"
|
|
elif [[ $line =~ ^Perms:.*r.*:.*:.*\(/(\{?,?\*\*,*\}?)\) ]]; then
|
|
skip_profile="All files available"
|
|
elif [[ $line =~ ^[[:space:]]*Attachment:[[:space:]]*(.+) ]]; then
|
|
attachment="${BASH_REMATCH[1]}"
|
|
[[ $attachment == "<NULL>" ]] && skip_profile="no attachment"
|
|
elif [[ $line == ---\ Entries\ --- ]]; then
|
|
in_entries=1
|
|
elif [[ $in_entries -ne 0 ]]; then
|
|
block_lines+=("$line")
|
|
fi
|
|
done
|
|
|
|
# Last profile
|
|
finish_profile "$curr_name" "$prof_file" "$skip_profile" "$attachment" block_lines
|
|
}
|
|
|
|
if (( $# < 1 )); then
|
|
echo "Usage: $0 <profile-file> [parser_extra_args]"
|
|
exit 1
|
|
fi
|
|
|
|
process_profile $@
|