You are not logged in.
Hi, brooding over Linux capabilities, I was trying to follow an
example about file capabilities, along the lines of [1]. The argument
is, that `ping` works for unprivileged users, without needing the SUID
bit set, because it comes the `cap_net_raw`.
What I do not understand: `ping` works just as well without any
capabilities. Here's the setup:
Unprivileged user:
$ id
uid=1001(foo) gid=100(users) groups=100(users)
The shell has no capabilities:
$ grep Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000000ffffffffff
CapAmb: 0000000000000000
The usuual `ping` works:
$ ping -c1 192.168.0.161
PING 192.168.0.161 (192.168.0.161) 56(84) bytes of data.
64 bytes from 192.168.0.161: icmp_seq=1 ttl=64 time=3.31 ms
--- 192.168.0.161 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 3.309/3.309/3.309/0.000 ms
because its executable comes with privileges:
$ ls -l "$(which ping)"
-rwxr-xr-x 1 root root 69k Nov 13 2019 /usr/bin/ping # printed red
$ getcap "$(which ping)"
/usr/bin/ping = cap_net_raw+ep
Make my own copy of `ping`, without privileges:
$ cp "$(which ping)" /tmp/foo
$ getcap /tmp/foo # no output
$ l /tmp/foo
-rwx------ 1 foo users 69k Aug 17 11:28 /tmp/foo # no SUID bit
So why does the following work?
$ /tmp/foo -c1 192.168.0.161
PING 192.168.0.161 (192.168.0.161) 56(84) bytes of data.
64 bytes from 192.168.0.161: icmp_seq=1 ttl=64 time=2.88 ms
--- 192.168.0.161 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.876/2.876/2.876/0.000 ms
I would have expected a complaint. (which is actually the case on a
SuSE SLES 12 system).
Can anyone reproduce? Did I get something wrong?
Cheers
Stefan
[1]: https://blog.container-solutions.com/li … n-practice
Last edited by stefan (2020-08-18 20:07:29)
Offline
Offline
What does that mean? What does it do? By which mechanics does it work?
Offline
# ping(8) without CAP_NET_ADMIN and CAP_NET_RAW
# The upper limit is set to 2^31-1. Values greater than that get rejected by
# the kernel because of this definition in linux/include/net/ping.h:
# #define GID_T_MAX (((gid_t)~0U) >> 1)
# That's not so bad because values between 2^31 and 2^32-1 are reserved on
# systemd-based systems anyway: https://systemd.io/UIDS-GIDS.html#summary
-net.ipv4.ping_group_range = 0 2147483647
net.ipv4.ping_group_range is documented in https://www.kernel.org/doc/Documentatio … sysctl.txt
Offline
Thanks. I'll try to put this into more words, correct me if I got it wrong...
So there's this file with that line:
$ grep net.ipv4.ping_group_range /usr/lib/sysctl.d/*
/usr/lib/sysctl.d/50-default.conf:-net.ipv4.ping_group_range = 0 2147483647
According to sysctl.d(5), this configures kernel parameters at boot. The parameter in question is `net.ipv4.ping_group_range`. The leading dash seems to suppress error messages if the parameter would not exist in the kernel.
The two integers describe a range of GIDs, members of which may “create ping sockets”, i.e.
$ sysctl net.ipv4.ping_group_range
net.ipv4.ping_group_range = 0 2147483647
allows everyone who's a member of a group with 0 ≤ GID ≤ 2147483647 to ping.
A rationale for having this may be to make ping work inside rootless Podman containers, but I do not know if that's the only one, nor whether there's a reason to have this by default.
Offline
Nah, not yet. I need to post a follow-up question: Trying to reproduce the example from my previous postings, I've disabled `ping_group_range` as documented, to exclude all groups from the exemption:
# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),19(log)
# sysctl net.ipv4.ping_group_range='1 0'
net.ipv4.ping_group_range = 1 0
# ping -c1 localhost
PING localhost(localhost (::1)) 56 data bytes
64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.066 ms
--- localhost ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.066/0.066/0.066/0.000 ms
# getcap $(which ping)
/usr/bin/ping = cap_net_raw+ep
And as unprivileged user:
$ id
uid=1001(foo) gid=100(users) groups=100(users)
$ sysctl net.ipv4.ping_group_range
net.ipv4.ping_group_range = 1 0
$ ping -c1 localhost
PING localhost(localhost (::1)) 56 data bytes
64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.055 ms
--- localhost ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.055/0.055/0.055/0.000 ms
$ getcap $(which ping)
/usr/bin/ping = cap_net_raw+ep
So`ping` still works, as expected due to having `cap_net_raw`. So let's trythis with our own copy of ping:
$ cp $(which ping) /tmp/foo
$ chmod 700 /tmp/foo
$ getcap /tmp/foo # no output
$ /tmp/foo -c1 localhost
/tmp/foo: socket: Operation not permitted
So far, so good. But why does the following *not* work:
# setcap cap_net_raw+ep /tmp/foo # NOTE: performed by root
$ getcap /tmp/foo
/tmp/foo = cap_net_raw+ep
$ /tmp/foo -c1 localhost
/tmp/foo: socket: Operation not permitted
Offline
/tmp is mounted nosuid by default.
nosuid Do not honor set-user-ID and set-group-ID bits or file capabilities when executing programs from this filesystem.
Offline
Amazing. Yes, when I repeat the experiment, but copy `ping` to `$HOME` instead of `/tmp`, I observe the desired effect. I'm a little bit frustrated/scared by the multitude of variables that affect the permissions of a process (and I have not yet begun to dig into user namespace hierarchies)...
@loqs: Thank you very much!
Offline