xref: /aosp_15_r20/external/libcap/progs/quicktest.sh (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1#!/bin/bash
2#
3# Run through a series of tests to try out the various capability
4# manipulations possible through exec.
5#
6# [Run this as root in a root-enabled process tree.]
7
8try_capsh () {
9    echo "TEST: ./capsh $*"
10    ./capsh "$@"
11    if [ $? -ne 0 ]; then
12	echo FAILED
13	return 1
14    else
15	echo PASSED
16	return 0
17    fi
18}
19
20fail_capsh () {
21    echo -n "EXPECT FAILURE: "
22    try_capsh "$@"
23    if [ $? -eq 1 ]; then
24	echo "[WHICH MEANS A PASS!]"
25	return 0
26    else
27	echo "Undesired result - aborting"
28	echo "PROBLEM TEST: $*"
29	exit 1
30    fi
31}
32
33pass_capsh () {
34    echo -n "EXPECT SUCCESS: "
35    try_capsh "$@"
36    if [ $? -eq 0 ]; then
37	return 0
38    else
39	echo "Undesired result - aborting"
40	echo "PROBLEM TEST: $*"
41	exit 1
42    fi
43}
44
45pass_capsh --print
46pass_capsh --current
47
48# Validate that PATH expansion works
49PATH=$(/bin/pwd)/junk:$(/bin/pwd) capsh == == == --modes
50if [ $? -ne 0 ]; then
51    echo "Failed to execute capsh consecutively for capability manipulation"
52    exit 1
53fi
54
55# Make a local non-setuid-0 version of capsh and call it privileged
56cp ./tcapsh-static ./privileged && /bin/chmod -s ./privileged
57if [ $? -ne 0 ]; then
58    echo "Failed to copy capsh for capability manipulation"
59    exit 1
60fi
61
62# Give it the forced capability it could need
63./setcap all=ep ./privileged
64if [ $? -ne 0 ]; then
65    echo "Failed to set all capabilities on file"
66    exit 1
67fi
68./setcap cap_setuid,cap_setgid=ep ./privileged
69if [ $? -ne 0 ]; then
70    echo "Failed to set limited capabilities on privileged file"
71    exit 1
72fi
73
74# validate libcap modes:
75pass_capsh --inh=cap_chown --mode=PURE1E --print --inmode=PURE1E
76pass_capsh --mode=NOPRIV --print --inmode=NOPRIV
77pass_capsh --mode=PURE1E --print --mode=NOPRIV --inmode=NOPRIV
78fail_capsh --mode=NOPRIV --print --mode=PURE1E
79fail_capsh --user=nobody --mode=NOPRIV --print -- ./privileged
80
81# simple IAB setting (no ambient) in pure1e mode.
82pass_capsh --mode=PURE1E --iab='!%cap_chown,cap_setuid'
83
84# Explore keep_caps support
85pass_capsh --keep=0 --keep=1 --keep=0 --keep=1 --print
86
87/bin/rm -f tcapsh
88/bin/cp tcapsh-static tcapsh
89/bin/chown root.root tcapsh
90/bin/chmod u+s tcapsh
91/bin/ls -l tcapsh
92
93# leverage keep caps to maintain capabilities across a change of euid
94# from setuid root to capable luser (as per wireshark/dumpcap 0.99.7)
95# This test is subtle. It is testing that a change to self, dropping
96# euid=0 back to that of the luser keeps capabilities.
97pass_capsh --uid=1 -- -c "./tcapsh --keep=1 --caps=\"cap_net_raw,cap_net_bind_service=ip\" --print --uid=1 --print --caps=\"cap_net_raw,cap_net_bind_service=pie\" --print"
98
99# this test is a change of user to a new user, note we need to raise
100# the cap_setuid capability (libcap has a function for that) in this case.
101pass_capsh --uid=1 -- -c "./tcapsh --caps=\"cap_net_raw,cap_net_bind_service=ip cap_setuid=p\" --print --cap-uid=2 --print --caps=\"cap_net_raw,cap_net_bind_service=pie\" --print"
102
103# This fails, on 2.6.24, but shouldn't
104pass_capsh --uid=1 -- -c "./tcapsh --keep=1 --caps=\"cap_net_raw,cap_net_bind_service=ip\" --uid=1 --forkfor=10 --caps= --print --killit=9 --print"
105
106# only continue with these if --secbits is supported
107./capsh --secbits=0x2f > /dev/null 2>&1
108if [ $? -ne 0 ]; then
109    echo "unable to test securebits manipulation - assume not supported (PASS)"
110    rm -f tcapsh
111    rm -f privileged
112    exit 0
113fi
114
115# nobody's uid. Static compilation of the capsh binary can disable pwd
116# info discovery.
117nouid=$(/usr/bin/id nobody -u)
118
119pass_capsh --secbits=42 --print
120fail_capsh --secbits=32 --keep=1 --keep=0 --print
121pass_capsh --secbits=10 --keep=0 --keep=1 --print
122fail_capsh --secbits=47 -- -c "./tcapsh --uid=$nouid"
123
124/bin/rm -f tcapsh
125
126# Suppress uid=0 privilege
127fail_capsh --secbits=47 --print -- -c "./capsh --uid=$nouid"
128
129# suppress uid=0 privilege and test this privileged
130pass_capsh --secbits=0x2f --print -- -c "./privileged --uid=$nouid"
131
132# observe that the bounding set can be used to suppress this forced capability
133fail_capsh --drop=cap_setuid --secbits=0x2f --print -- \
134	   -c "./privileged --uid=$nouid"
135
136# observe that effective cap_setpcap is required to drop bset
137fail_capsh --caps="=ep cap_setpcap-ep" --drop=cap_setuid --current
138pass_capsh --strict --caps="cap_setpcap=ep" --drop=cap_setuid --current
139fail_capsh --strict --caps="cap_setpcap=p" --drop=cap_setuid --current
140fail_capsh --strict --caps="=ep cap_setpcap-e" --drop=cap_setuid --current
141
142# observe that effective cap_setpcap is required to raise non-p bits
143fail_capsh --strict --caps="cap_setpcap=p" --inh=cap_chown --current
144# non-strict mode and capsh figures it out
145pass_capsh --caps="cap_setpcap=p" --inh=cap_chown --current
146
147# permitted bits can be raised in inheritable flag without being effective.
148pass_capsh --strict --caps="cap_chown=p" --inh=cap_chown --current
149
150# change the way the capability is obtained (make it inheritable)
151./setcap cap_setuid,cap_setgid=ei ./privileged
152
153# Note, the bounding set (edited with --drop) only limits p
154# capabilities, not i's.
155pass_capsh --secbits=47 --inh=cap_setuid,cap_setgid --drop=cap_setuid \
156    --uid=1 --print -- -c "./privileged --uid=$nouid"
157
158# test that we do not support capabilities on setuid shell-scripts
159/bin/cat > hack.sh <<EOF
160#!/bin/bash
161/usr/bin/id
162mypid=\$\$
163caps=\$(./getpcaps \$mypid 2>&1 | /usr/bin/cut -d: -f2)
164if [ "\$caps" != " =" ]; then
165  echo "Shell script got [\$caps] - you should upgrade your kernel"
166  exit 1
167else
168  ls -l \$0
169  echo "Good, no capabilities [\$caps] for this setuid-0 shell script"
170fi
171exit 0
172EOF
173/bin/chmod +xs hack.sh
174./capsh --uid=1 --inh=none --print -- ./hack.sh
175status=$?
176/bin/rm -f ./hack.sh
177if [ $status -ne 0 ]; then
178    echo "shell scripts can have capabilities (bug)"
179    exit 1
180fi
181
182# Max lockdown (ie., pure capability model as POSIX.1e intended).
183secbits=0x2f
184if ./capsh --has-ambient ; then
185    secbits="0xef --noamb"
186fi
187pass_capsh --keep=1 --uid=$nouid --caps=cap_setpcap=ep \
188	   --drop=all --secbits=$secbits --caps= --print
189
190# Verify we can chroot
191pass_capsh --chroot=$(/bin/pwd)
192pass_capsh -- -c "./tcapsh-static --chroot=$(/bin/pwd) =="
193fail_capsh --chroot=$(/bin/pwd) -- -c "echo oops"
194
195./capsh --has-ambient
196if [ $? -eq 0 ]; then
197    echo "test ambient capabilities"
198
199    # Ambient capabilities (any file can inherit capabilities)
200    pass_capsh --noamb
201
202    # test that shell scripts can inherit through ambient capabilities
203    /bin/cat > hack.sh <<EOF
204#!/bin/bash
205/usr/bin/id
206mypid=\$\$
207caps=\$(./getpcaps \$mypid 2>&1 | /usr/bin/cut -d: -f2)
208if [ "\$caps" != " = cap_setuid+i" ]; then
209  echo "Shell script got [\$caps]"
210  exit 0
211fi
212ls -l \$0
213echo "no capabilities [\$caps] for this shell script"
214exit 1
215EOF
216    /bin/chmod +x hack.sh
217    pass_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- \
218	       ./hack.sh
219
220    /bin/rm -f hack.sh
221
222    # Next force the privileged binary to have an empty capability set.
223    # This is sort of the opposite of privileged - it should ensure that
224    # the file can never acquire privilege by the ambient method.
225    ./setcap = ./privileged
226    fail_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- \
227	       -c "./privileged --print --uid=1"
228
229    pass_capsh --keep=1 --uid=$nouid --strict \
230	       --caps="cap_setuid=p cap_setpcap=ep" \
231	       --inh=cap_setuid --addamb=cap_setuid --current
232
233    # No effective capabilities are needed to raise or lower ambient values.
234    pass_capsh --keep=1 --uid=$nouid --strict --caps="cap_setuid=p" \
235	       --inh=cap_setuid --addamb=cap_setuid --current
236    pass_capsh --keep=1 --uid=$nouid --strict --iab="!^cap_setuid" \
237	       --caps="cap_setuid=pi" --current --delamb=cap_setuid --current
238
239
240    # finally remove the capability from the privileged binary and try again.
241    ./setcap -r ./privileged
242    pass_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- \
243	       -c "./privileged --print --uid=1"
244
245    # validate IAB setting with an ambient capability
246    pass_capsh --iab='!%cap_chown,^cap_setpcap,cap_setuid'
247    fail_capsh --mode=PURE1E --iab='!%cap_chown,^cap_setuid'
248fi
249/bin/rm -f ./privileged
250
251echo "testing namespaced file caps"
252
253# nsprivileged capsh will have an ns rootid value (this is
254# the same setup as an earlier test but with a ns file cap).
255rm -f nsprivileged
256cp ./tcapsh-static ./nsprivileged && /bin/chmod -s ./nsprivileged
257./setcap -n 1 all=ep ./nsprivileged
258if [ $? -eq 0 ]; then
259    ./getcap -n ./nsprivileged | grep -F "[rootid=1]"
260    if [ $? -ne 0 ]; then
261	echo "FAILED setting ns rootid on file"
262	exit 1
263    fi
264    # since this is a ns file cap and not a regular one, it should not
265    # lead to a privilege escalation outside of the namespace it
266    # refers to. We suppress uid=0 privilege and confirm this
267    # nsprivileged binary does not have the power to change uid.
268    fail_capsh --secbits=$secbits --print -- -c "./nsprivileged --uid=$nouid"
269else
270    echo "ns file caps not supported - skipping test"
271fi
272rm -f nsprivileged
273
274# If the build tree compiled the Go cap package.
275if [ -f ../go/compare-cap ]; then
276    cp ../go/compare-cap .
277    LD_LIBRARY_PATH=../libcap ./compare-cap
278    if [ $? -ne 0 ]; then
279	echo "FAILED to execute go binary"
280	exit 1
281    fi
282    LD_LIBRARY_PATH=../libcap ./compare-cap 2>&1 | \
283	grep "skipping file cap tests"
284    if [ $? -eq 0 ]; then
285	echo "FAILED not engaging file cap tests"
286	exit 1
287    fi
288    echo "PASSED"
289else
290    echo "no Go support compiled, so skipping Go tests"
291fi
292rm -f compare-cap
293
294echo "attempt to exploit kernel bug"
295./uns_test
296if [ $? -ne 0 ]; then
297    echo "upgrade your kernel"
298    exit 1
299fi
300
301echo "ALL TESTS PASSED!"
302