💾 Archived View for thrig.me › blog › 2024 › 02 › 08 › interface-equivalence.gmi captured on 2024-08-18 at 18:57:19. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2024-03-21)
-=-=-=-=-=-=-
Another question from a chat is whether doas(1) is equivalent to using su(1), assuming both calls do the same thing and that you don't care whose password is being supplied. And the short answer is, no. There are (at present) interface inconsistencies on OpenBSD.
http://man.openbsd.org/man1/doas.1
http://man.openbsd.org/man1/su.1
Let's assume we want a root shell, and that we want to preserve the environment. Whether either of these goals is a good idea is not addressed here; there are probably good reasons not to carry random environment variables along, and probably also good reasons not to shell up as root. Opinions vary here.
Let us assume that we have "keepenv" set for doas, and do similar in su to preserve the environment of the caller.
$ export FOO=bar $ doas -s ... $ echo $FOO bar $ exit $ su ... $ echo $FOO bar $ exit
Where's the problem? A test may not suffice to reveal differences. In particular the above only sets the environment variable once, while in fact environment variables can be set many times.
Duplicate Environment Variables
Now with a handy "dupenv" program,
$ unset FOO $ dupenv FOO=first FOO=middle FOO=last doas -s ... $ echo $FOO first $ env | grep FOO FOO=first $ exit $ dupenv FOO=first FOO=middle FOO=last su ... $ echo $FOO last $ env | grep FOO FOO=last $ exit
We find that the shell invoked by doas has the first of the duplicate environment variables, while the shell invoked by su has the last. Also the duplicates have been filtered out, which may not be the case for other programs on other unix systems. (Old versions of sudo did not deal with duplicate environment variables, whoops!)
Theo and Todd bring up good points:
https://marc.info/?l=openbsd-tech&m=170727265806407&w=2
in particular that su is not altering the environment, while doas does. ksh(1) on OpenBSD picks the last of any duplicates as it iterates through the **environ list and sets shell variables. getenv(3) picks the first of any duplicates. Implementations may well vary here.
$ cat whatget.c #include <stdio.h> #include <stdlib.h> int main(void) { printf("%s\n", getenv("FOO")); } $ make whatget cc -O2 -pipe -o whatget whatget.c $ dupenv FOO=first FOO=middle FOO=last ./whatget first
Probably one should sanitize the environment by default, especially if security or consistency are on the line. Otherwise who knows what you are preserving from where and how it will be used. Whether and if so how to get to root is more open to debate.
The security implications here are pretty minor; if an attacker can set arbitrary environment variables to pass through doas there's usually far worse they can already do. Usually bad environment variables are along the lines of "some weird env got carried over from a macbook and then mailman's python blew up on it with a strange error that took a while to debug". There is however the potential that an attacker, maybe via some plugin interface, can set a suitable LD_LIBRARY_PATH or such and then get the wrong duplicate environment variable through to something and can escalate their access from there.