NetworkConfiguration / dhcpcd

DHCP / IPv4LL / IPv6RA / DHCPv6 client.
https://roy.marples.name/projects/dhcpcd
BSD 2-Clause "Simplified" License
346 stars 111 forks source link

dhcpcd issue when compiled with glibc 2.36 #120

Closed maravtdm closed 2 years ago

maravtdm commented 2 years ago

Hi Roy,

OS: Slackware current glibc: 2.36 kernel : 5.19.1

I have an issue with dhcpcd ( 9.4.1 and the master branch) Attached: /var/log/messages when I restarted NetworkManager (with dhcpcd 9.99.0)

Let me know if you need anything more dhcpcd_messages.txt

rsmarples commented 2 years ago

Looks like we need to add a define for __NR_getrandom in privsep-linux.c

I can prepare a patch when I get back from holiday next week unless someone else can beat me to it.

ido commented 2 years ago

This might be a byproduct of getrandom() moving to a vDSO/vsyscall64. More information here: https://lore.kernel.org/lkml/20220729145525.1729066-1-Jason@zx2c4.com/

ernibert commented 2 years ago

Hi Roy, Hi Ido,

yes, there seems to be an issue with getrandom(). Find attached some logs i captured. The 000-build-dhcpcd shows how i built it, the other next two (001 and 005) are captures of the error and finally a normal working client in 010.

To get it work, just a small sed has been used: case "$(uname -m)" in i?86) sed -e '/Deny everything else/i SECCOMP_ALLOW(__NR_getrandom),' -i src/privsep-linux.c ;; esac

It does the patch just for i686, but that was just for testing. If applies to i686 and x86_64. I should mention that the command i used to test was dhcpcd -B -d -4 eth0

dhcpcd-seccomp-getrandom.zip .

xry111 commented 2 years ago

Some of us (@lfs-book) can reproduce the issue on x86_64, but some cannot. We are not sure how to "100%" reproduce the issue, but I can tell what's happening here.

dhcpcd building system checks if <stdlib.h> provides arc4random function and its friends. If they are provided, dhcpcd will use it. Otherwise, dhcpcd will use an internal shipped version as fallback.

Glibc-2.36 added arc4random into <stdlib.h>, so dhcpcd will use the arc4random-family functions in Glibc. Currently these functions are simple wrappers around getrandom system call (Glibc developers have considered a pure user-space implementation, but the conclusion was there is no way to determine if the RNG should be reseeded without some information only known by the kernel). But dhcpcd's seccomp filter does not allow getrandom, causing the crash.

sed -e '/Deny everything else/i SECCOMP_ALLOW(__NR_getrandom),' -i src/privsep-linux.c does the job for both 32-bit x86 and x86_64.

xry111 commented 2 years ago

To me use a system call allowlist (rejecting anything you unexpect) is almost always wrong in nature. We depend on external libraries (libc, at least) and we can't control what system call the library will invoke. Sometimes libc (and other libraries) invokes a system call in a manner we cannot expect. (For example, glibc invokes sysinfo in qsort implementation to improve performance, did you expect it? :)

If you want to use an allowlist, either (1) program everything yourself, don't use libc and invoke system calls with assembly code; (2) at least, don't kill the program if some system call you cannot expect is invoked, return ENOSYS (via SECCOMP_RET_ERRNO) instead. Then the libraries (if they are programmed carefully) will manage to work around the issue, just like how they are running on a relatively old kernel.

OTOH, use a denylist for some really dangerous thing (pciconfig_write for example) is mostly OK.

rsmarples commented 2 years ago

To me use a system call allowlist (rejecting anything you unexpect) is almost always wrong in nature. We depend on external libraries (libc, at least) and we can't control what system call the library will invoke. Sometimes libc (and other libraries) invokes a system call in a manner we cannot expect. (For example, glibc invokes sysinfo in qsort implementation to improve performance, did you expect it? :)

If you want to use an allowlist, either (1) program everything yourself, don't use libc and invoke system calls with assembly code; (2) at least, don't kill the program if some system call you cannot expect is invoked, return ENOSYS (via SECCOMP_RET_ERRNO) instead. Then the libraries (if they are programmed carefully) will manage to work around the issue, just like how they are running on a relatively old kernel.

OTOH, use a denylist for some really dangerous thing (pciconfig_write for example) is mostly OK.

This is a design issue with SECCOMP which I noted here: https://github.com/NetworkConfiguration/dhcpcd/blob/e307ec2c58d6beb0c48d34253c19eac3b9851f07/src/privsep-linux.c#L393

Sadly the capsicum port to Linux stalled.

maravtdm commented 2 years ago

Thank you Roy

ido commented 2 years ago

Hi Xi,

I think you have the balance of harms wrong, or aren’t considering the real risk to security of a denylist approach.

It’s reasonable to assume your language runtime (libc) will remain stable, and use the same syscalls, and to handle exceptions as they arise. In this case, about twice a decade. Denylists defeat the purpose of the security mechanism, because nearly every syscall you miss - including newly introduced ones across every kernel/kernel version in existence - will be impossible to enumerate and often present an unreasonably large attack surface.

It’s super easy to enumerate syscalls we do use - both through static analysis and at runtime using tools like strace.

On Thu, Aug 25, 2022 at 19:40 Xi Ruoyao @.***> wrote:

To me use a system call allowlist (rejecting anything you unexpect) is almost always wrong in nature. We depends on external libraries (libc, at least) and we can't control what system call the library will invoke. Sometimes libc (and other libraries) invokes a system call in a manner we cannot expect. (For example, glibc invokes sysinfo in qsort implementation to improve performance, did you expect it? :)

If you want to use an allowlist, either (1) program everything yourself, don't use libc and invoke system calls with assembly code; (2) at least, don't kill the program if some system call you cannot expect is invoked, return ENOSYS (via SECCOMP_RET_ERRNO) instead. Then the libraries (if they are programmed carefully) will manage to work around the issue, just like how they are running on a relatively old kernel.

OTOH, use a denylist for some really dangerous thing (pciconfig_write for example) is mostly OK.

— Reply to this email directly, view it on GitHub https://github.com/NetworkConfiguration/dhcpcd/issues/120#issuecomment-1227980583, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAU4RXI3GXOFZFVMYYPSVDV3AVCTANCNFSM56VVOIBA . You are receiving this because you commented.Message ID: @.***>

xry111 commented 2 years ago

On Tue, 2022-10-11 at 00:28 -0700, Ido Rosen wrote:

It’s reasonable to assume your language runtime (libc) will remain stable, and use the same syscalls

It's not only language runtime, but also other libraries.

We keep using Glibc for 20 years, but not the same version of Glibc. And "what syscall Glibc uses" is definitely not a part of its public ABI.

and to handle exceptions as they arise.

Then you should use SECCOMP_RET_ERRNO instead of SECCOMP_RETKILL{PROCESS,THREAD} or SECCOMP_RET_TRAP, to give the runtime a chance to handle the exceptions.

-- Xi Ruoyao @.***> School of Aerospace Science and Technology, Xidian University

rsmarples commented 2 years ago

Then you should use SECCOMP_RET_ERRNO instead of SECCOMP_RETKILL{PROCESS,THREAD} or SECCOMP_RET_TRAP, to give the runtime a chance to handle the exceptions.

If you compile dhcpcd with SECCOMP_FILTER_DEBUG then SECCOMP_RET_TRAP is used instead. https://github.com/NetworkConfiguration/dhcpcd/blob/master/src/privsep-linux.c#L143

We also install a signal handler to log a suitable diagnostic before exiting. https://github.com/NetworkConfiguration/dhcpcd/blob/master/src/privsep-linux.c#L446

If dhcpcd detects a security issue the only correct action is to exit.