>Synopsis: pledge allows /dev/null to be any file type
>Category: kernel
>Environment:
System : OpenBSD 7.2
Details : OpenBSD 7.2 (GENERIC.MP) #2: Thu Nov 24 23:53:03 MST 2022
[email protected]:/usr/src/sys/arch/arm64/compile/GENERIC.MP
Architecture: OpenBSD.arm64
Machine : arm64
>Description:
I was testing pledge on a 7.2 system and as a test opened /dev/null.
I was astonished that it didn't abort. OK perhaps it needs to do that but
doesn't it work better if /dev/null is major/minor (2,2) device? I have a
ktrace for you to show what I mean.
>How-To-Repeat:
spica# mkdir dev
mkdir: dev: File exists
spica# touch dev/null
spica# ktrace -i ./testprog
spica# ls -l dev/null
-rw-r--r-- 1 root pjp 5 Mar 19 22:51 dev/null
spica# cat dev/null
test
spica#
The ktrace I'm gonna edit it to show only the juicy parts:
13252 testprog CALL chroot(0x995cc0ea640)
13252 testprog NAMI "/home/pjp"
13252 testprog RET chroot 0
13252 testprog CALL kbind(0x7f7fffff95b8,24,0xd10fcc1b312a79c0)
13252 testprog RET kbind 0
13252 testprog CALL chdir(0x995cc0ea64a)
13252 testprog NAMI "/"
13252 testprog RET chdir 0
13252 testprog CALL kbind(0x7f7fffff95b8,24,0xd10fcc1b312a79c0)
13252 testprog RET kbind 0
13252 testprog CALL pledge(0x995cc0ea652,0)
13252 testprog STRU promise="stdio"
13252 testprog RET pledge 0
13252 testprog CALL kbind(0x7f7fffff95b8,24,0xd10fcc1b312a79c0)
13252 testprog RET kbind 0
13252 testprog CALL open(0x995cc0ea658,0x1<O_WRONLY>)
13252 testprog NAMI "/dev/null"
13252 testprog RET open 4
13252 testprog CALL kbind(0x7f7fffff95b8,24,0xd10fcc1b312a79c0)
13252 testprog RET kbind 0
13252 testprog CALL write(4,0x995cc0ea64c,0x5)
13252 testprog GIO fd 4 wrote 5 bytes
"test
So writing to a file called {CHROOT}/dev/null is allowed on stdio pledge.
This is very suboptimal to me. Can't it perform a check for major 2, minor 2?
spica# ls -l /dev/null
crw-rw-rw- 1 root wheel 2, 2 Mar 19 10:14 /dev/null
>Fix:
>From github I got this for the HEAD of CVS from:
https://github.com/openbsd/src/blob/master/sys/kern/kern_pledge.c
----->
case SYS_open:
/* daemon(3) or other such functions */
if ((ni->ni_pledge & ~(PLEDGE_RPATH | PLEDGE_WPATH)) == 0 &&
strcmp(path, "/dev/null") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
<------
I figure that's the code for this, partially. But someone else would surely
know better. And has surely a better fix on hand?
dmesg:
see previous reports.