#!/bin/bash # Copyright (c) 2012 The Chromium OS Authors. # # Based on: # http://bazaar.launchpad.net/~ubuntu-bugcontrol/qa-regression-testing/master/view/head:/scripts/kernel-security/ptrace/ptrace-restrictions.sh # Copyright (C) 2010-2011 Canonical Ltd. # License: GPLv3 # Author: Kees Cook <kees.cook@canonical.com> set -e if [ "$(whoami)" = "root" ]; then echo "Cannot be root for this test" >&2 exit 1 fi export LANG=C pid= dir= function start_sleeper() { dir=$(mktemp -d -t sleeper-XXXXXX) mkfifo "$dir"/status ./sleeper "$1" 120 >"$dir"/status & pid=$! # Wait for sleeper to start up. read status < "$dir"/status } function kill_sleeper() { disown $pid kill $pid rm -rf "$dir" } rc=0 # Check we can see direct children. OUT=$(gdb -ex run -ex quit --batch ./sleeper </dev/null 2>&1) if echo "$OUT" | grep -q 'Quit anyway'; then echo "ok: children correctly allow ptrace" else echo "FAIL: Children unexpectedly not allow ptrace" rc=1 fi # Check we can't see cousins. sleep 120 & pid=$! OUT=$(gdb -ex "attach $pid" -ex "quit" --batch </dev/null 2>&1) if echo "$OUT" | grep -q 'Operation not permitted'; then echo "ok: cousins correctly not allow ptrace" else echo "FAIL: cousins unexpectedly allow ptrace" rc=1 fi # Validate we can see cousin /proc entries. if ls -la /proc/$pid/exe >/dev/null 2>&1; then echo "ok: cousins correctly visible in /proc" else echo "FAIL: cousins unexpectedly invisible in /proc" rc=1 fi # Check we can't attach to init. OUT=$(gdb -ex "attach 1" -ex "quit" --batch </dev/null 2>&1) if echo "$OUT" | grep -q 'Operation not permitted'; then echo "ok: init correctly not allowing ptrace" else echo "FAIL: init unexpectedly allowed ptrace" rc=1 fi # Check we can't see init. if ! ls -la /proc/1/exe >/dev/null 2>&1; then echo "ok: init correctly invisible in /proc" else echo "FAIL: init unexpectedly visible in /proc" rc=1 fi # Drop the sleep process and destroy it without disrupting the shell. disown $pid kill $pid # Validate that prctl(PR_SET_PTRACER, 0, ...) works to delete tracer. start_sleeper 0 OUT=$(gdb -ex "attach $pid" -ex "quit" --batch </dev/null 2>&1) prctl="prctl(PR_SET_PTRACER, 0, ...)" if echo "$OUT" | grep -q 'Operation not permitted'; then echo "ok: $prctl correctly not allowed ptrace" else echo "FAIL: $prctl unexpectedly allowed ptrace" rc=1 fi kill_sleeper # Validate near ancestor allowed with PR_SET_PTRACER use. start_sleeper $$ OUT=$(gdb -ex "attach $pid" -ex "quit" --batch </dev/null 2>&1) prctl="prctl(PR_SET_PTRACER, parent, ...)" if echo "$OUT" | grep -q 'Quit anyway'; then echo "ok: $prctl correctly allowed ptrace" else echo "FAIL: $prctl unexpectedly not allowed ptrace" rc=1 fi kill_sleeper # Validate distant ancestor allowed with PR_SET_PTRACER use. start_sleeper 1 OUT=$(gdb -ex "attach $pid" -ex "quit" --batch </dev/null 2>&1) prctl="prctl(PR_SET_PTRACER, 1, ...)" if echo "$OUT" | grep -q 'Quit anyway'; then echo "ok: $prctl correctly allowed ptrace" else echo "FAIL: $prctl unexpectedly not allowed ptrace" rc=1 fi kill_sleeper # Validate -1 disables protection. start_sleeper -1 OUT=$(gdb -ex "attach $pid" -ex "quit" --batch </dev/null 2>&1) prctl="prctl(PR_SET_PTRACER, -1, ...)" if echo "$OUT" | grep -q 'Quit anyway'; then echo "ok: $prctl correctly allowed ptrace" else echo "FAIL: $prctl unexpectedly not allowed ptrace" rc=1 fi kill_sleeper exit $rc