229 lines
5.8 KiB
Bash
Executable File
229 lines
5.8 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
cc_bin=${CC_BIN:-/usr/bin/cc}
|
|
uid1=${BUILD_UID1:-35001}
|
|
gid1=${BUILD_GID1:-35001}
|
|
uid2=${BUILD_UID2:-35002}
|
|
gid2=${BUILD_GID2:-35002}
|
|
hold_seconds=${HOLD_SECONDS:-2}
|
|
cleanup_workdir=0
|
|
mount_points=
|
|
jail_ids=
|
|
|
|
if [ -n "${WORKDIR:-}" ]; then
|
|
workdir=$WORKDIR
|
|
mkdir -p "$workdir"
|
|
else
|
|
workdir=$(mktemp -d /tmp/fruix-privdrop-prototype.XXXXXX)
|
|
cleanup_workdir=1
|
|
fi
|
|
|
|
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
|
|
cleanup_workdir=0
|
|
fi
|
|
|
|
record_mount() {
|
|
mount_points="$1
|
|
$mount_points"
|
|
}
|
|
|
|
record_jail() {
|
|
jail_ids="$1
|
|
$jail_ids"
|
|
}
|
|
|
|
cleanup() {
|
|
set +e
|
|
|
|
old_ifs=$IFS
|
|
IFS='
|
|
'
|
|
for jail_id in $jail_ids; do
|
|
[ -n "$jail_id" ] || continue
|
|
sudo jail -r "$jail_id" >/dev/null 2>&1 || true
|
|
done
|
|
for mount_point in $mount_points; do
|
|
[ -n "$mount_point" ] || continue
|
|
sudo umount "$mount_point" >/dev/null 2>&1 || true
|
|
done
|
|
IFS=$old_ifs
|
|
|
|
if [ "$cleanup_workdir" -eq 1 ]; then
|
|
sudo rm -rf "$workdir"
|
|
fi
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
helper_src=tests/daemon/freebsd-build-user-helper.c
|
|
helper_bin=$workdir/tools/freebsd-build-user-helper
|
|
protected_dir=$workdir/protected
|
|
outside_sentinel=$workdir/outside-sentinel
|
|
metadata_file=$workdir/freebsd-privdrop-metadata.txt
|
|
helper_compile_log=$workdir/helper-compile.log
|
|
helper_compile_err=$workdir/helper-compile.err
|
|
job1_out=$workdir/job1.out
|
|
job1_err=$workdir/job1.err
|
|
job2_out=$workdir/job2.out
|
|
job2_err=$workdir/job2.err
|
|
job1_stat=$workdir/job1.stat
|
|
job2_stat=$workdir/job2.stat
|
|
|
|
mkdir -p "$workdir/tools" "$protected_dir" "$workdir/job1-host" "$workdir/job2-host"
|
|
printf 'host-only-outside-sentinel\n' > "$outside_sentinel"
|
|
printf 'protected-root-owned\n' > "$protected_dir/root-note.txt"
|
|
chmod 0755 "$protected_dir"
|
|
chmod 0644 "$protected_dir/root-note.txt"
|
|
|
|
"$cc_bin" -Wall -Wextra -std=c11 "$helper_src" -o "$helper_bin" >"$helper_compile_log" 2>"$helper_compile_err"
|
|
|
|
prepare_job_tree() {
|
|
job_name=$1
|
|
job_uid=$2
|
|
job_gid=$3
|
|
peer_uid=$4
|
|
peer_gid=$5
|
|
|
|
job_host=$workdir/$job_name-host
|
|
root=$job_host/root
|
|
work_mount=$job_host/work
|
|
peer_mount=$job_host/peer
|
|
mkdir -p "$root/tools" "$root/protected" "$root/work" "$root/peer" "$root/tmp" "$work_mount" "$peer_mount"
|
|
chmod 1777 "$root/tmp"
|
|
|
|
printf '%s-allowed\n' "$job_name" > "$work_mount/allowed.txt"
|
|
printf '%s-secret\n' "$job_name" > "$work_mount/secret.txt"
|
|
chmod 0600 "$work_mount/allowed.txt" "$work_mount/secret.txt"
|
|
chmod 0700 "$work_mount"
|
|
sudo chown -R "$job_uid:$job_gid" "$work_mount"
|
|
|
|
printf 'peer-for-%s\n' "$job_name" > "$peer_mount/secret.txt"
|
|
chmod 0600 "$peer_mount/secret.txt"
|
|
chmod 0700 "$peer_mount"
|
|
sudo chown -R "$peer_uid:$peer_gid" "$peer_mount"
|
|
|
|
for host_path in /lib /libexec; do
|
|
sudo mkdir -p "$root$host_path"
|
|
sudo mount_nullfs -o ro "$host_path" "$root$host_path"
|
|
record_mount "$root$host_path"
|
|
done
|
|
|
|
sudo mount_nullfs -o ro "$workdir/tools" "$root/tools"
|
|
record_mount "$root/tools"
|
|
sudo mount_nullfs "$work_mount" "$root/work"
|
|
record_mount "$root/work"
|
|
sudo mount_nullfs -o ro "$peer_mount" "$root/peer"
|
|
record_mount "$root/peer"
|
|
sudo mount_nullfs "$protected_dir" "$root/protected"
|
|
record_mount "$root/protected"
|
|
|
|
jail_name=fruix-privdrop-$job_name-$$
|
|
jail_id=$(sudo jail -i -c \
|
|
name="$jail_name" \
|
|
path="$root" \
|
|
host.hostname="$jail_name" \
|
|
persist \
|
|
ip4=disable \
|
|
ip6=disable)
|
|
record_jail "$jail_id"
|
|
|
|
printf '%s\n' "$jail_id" > "$job_host/jid"
|
|
}
|
|
|
|
prepare_job_tree job1 "$uid1" "$gid1" "$uid2" "$gid2"
|
|
prepare_job_tree job2 "$uid2" "$gid2" "$uid1" "$gid1"
|
|
|
|
jid1=$(cat "$workdir/job1-host/jid")
|
|
jid2=$(cat "$workdir/job2-host/jid")
|
|
|
|
start_epoch=$(date +%s)
|
|
sudo jexec "$jid1" /tools/freebsd-build-user-helper \
|
|
--job-name job1 \
|
|
--uid "$uid1" \
|
|
--gid "$gid1" \
|
|
--allowed-file /work/allowed.txt \
|
|
--own-output /work/result.txt \
|
|
--peer-file /peer/secret.txt \
|
|
--hidden-file /outside-sentinel \
|
|
--protected-file /protected/escape-job1 \
|
|
--hold-seconds "$hold_seconds" >"$job1_out" 2>"$job1_err" &
|
|
pid1=$!
|
|
|
|
sudo jexec "$jid2" /tools/freebsd-build-user-helper \
|
|
--job-name job2 \
|
|
--uid "$uid2" \
|
|
--gid "$gid2" \
|
|
--allowed-file /work/allowed.txt \
|
|
--own-output /work/result.txt \
|
|
--peer-file /peer/secret.txt \
|
|
--hidden-file /outside-sentinel \
|
|
--protected-file /protected/escape-job2 \
|
|
--hold-seconds "$hold_seconds" >"$job2_out" 2>"$job2_err" &
|
|
pid2=$!
|
|
|
|
set +e
|
|
wait "$pid1"
|
|
rc1=$?
|
|
wait "$pid2"
|
|
rc2=$?
|
|
set -e
|
|
end_epoch=$(date +%s)
|
|
elapsed=$((end_epoch - start_epoch))
|
|
|
|
if [ "$rc1" -ne 0 ] || [ "$rc2" -ne 0 ]; then
|
|
echo "freebsd privilege-drop prototype failed" >&2
|
|
cat "$job1_out" >&2 || true
|
|
cat "$job1_err" >&2 || true
|
|
cat "$job2_out" >&2 || true
|
|
cat "$job2_err" >&2 || true
|
|
exit 1
|
|
fi
|
|
|
|
sudo stat -f '%Su %Sg %u %g %Sp' "$workdir/job1-host/work/result.txt" > "$job1_stat"
|
|
sudo stat -f '%Su %Sg %u %g %Sp' "$workdir/job2-host/work/result.txt" > "$job2_stat"
|
|
|
|
cat > "$metadata_file" <<EOF
|
|
workdir=$workdir
|
|
helper_src=$helper_src
|
|
helper_bin=$helper_bin
|
|
helper_compile_log=$helper_compile_log
|
|
helper_compile_err=$helper_compile_err
|
|
jail1_id=$jid1
|
|
jail2_id=$jid2
|
|
build_uid1=$uid1
|
|
build_gid1=$gid1
|
|
build_uid2=$uid2
|
|
build_gid2=$gid2
|
|
hold_seconds=$hold_seconds
|
|
elapsed_seconds=$elapsed
|
|
job1_rc=$rc1
|
|
job2_rc=$rc2
|
|
job1_result_stat=$(cat "$job1_stat")
|
|
job2_result_stat=$(cat "$job2_stat")
|
|
job1_output_begin
|
|
$(cat "$job1_out")
|
|
job1_output_end
|
|
job2_output_begin
|
|
$(cat "$job2_out")
|
|
job2_output_end
|
|
EOF
|
|
|
|
if [ -n "${METADATA_OUT:-}" ]; then
|
|
mkdir -p "$(dirname "$METADATA_OUT")"
|
|
cp "$metadata_file" "$METADATA_OUT"
|
|
fi
|
|
|
|
printf 'PASS freebsd-privilege-drop-prototype\n'
|
|
printf 'Working directory: %s\n' "$workdir"
|
|
printf 'Metadata file: %s\n' "$metadata_file"
|
|
if [ -n "${METADATA_OUT:-}" ]; then
|
|
printf 'Copied metadata to: %s\n' "$METADATA_OUT"
|
|
fi
|
|
printf '%s\n' '--- job1 output ---'
|
|
cat "$job1_out"
|
|
printf '%s\n' '--- job2 output ---'
|
|
cat "$job2_out"
|
|
printf 'Elapsed seconds: %s\n' "$elapsed"
|
|
printf '%s\n' '--- metadata ---'
|
|
cat "$metadata_file"
|