💾 Archived View for thrig.me › blog › 2024 › 02 › 08 › interface-equivalence.gmi captured on 2024-07-09 at 01:17:26. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2024-03-21)

-=-=-=-=-=-=-

Interface Equivalence: doas vs. su

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.