. "$BST_LIB" require sudo sudo/present # sudo splits cleanly into two halves on this image, and the suite is arranged # around that split rather than around the binary list. # # The half that works is the sudoers LANGUAGE: the parser, the policy file, the # format converters and the log tools. visudo really parses, really reports # syntax errors with a column and a caret, and cvtsudoers really converts the # same policy to JSON, LDIF and back. Those cases are ordinary assertions. # # The half that does not work is sudo RUNNING A COMMAND, and the cause is not # sudo. umask(2) is unwired on wasm32-hwjs and, because umask() is defined never # to fail, glibc's wrapper hands the raw -ENOSYS back as if it were the previous # mask. sudo reads the mask at startup (src/sudo.c:609), passes it to the policy # plugin as "umask=037777777732" (= (unsigned)-38), and sudoers' own # sudo_strtomode() rejects it as out of range -- so plugin init fails and no # command is ever run. That case is written to RUN and report what it finds, so # it turns green by itself when the syscall is wired instead of being commented # out and forgotten. T=${TMPDIR:-/tmp}/bst-sudo.$$ mkdir -p "$T" # /tmp is a real in-kernel tmpfs, not the FUSE-backed rootfs, so files created # here are genuinely root-owned in-guest whatever staged the image. sudo refuses # to read a policy file it does not trust the ownership of, so writing the # fixtures here is what keeps the parser cases independent of the staging path. printf 'Defaults env_reset\nroot ALL=(ALL:ALL) ALL\n%%wheel ALL=(ALL) NOPASSWD: ALL\n' > "$T/good" printf 'root ALL=(ALL:ALL ALL\n' > "$T/bad" chmod 440 "$T/good" "$T/bad" # ---- packaging ------------------------------------------------------------- check sudo/visudo-present test -x /usr/sbin/visudo check sudo/cvtsudoers-present test -x /usr/bin/cvtsudoers check sudo/sudoreplay-present test -x /usr/bin/sudoreplay check sudo/logsrvd-present test -x /usr/sbin/sudo_logsrvd check sudo/sendlog-present test -x /usr/sbin/sudo_sendlog check_out sudo/sudoedit-link "sudo" readlink /usr/bin/sudoedit check sudo/sudoers-installed test -f /etc/sudoers check sudo/sudoconf-installed test -f /etc/sudo.conf # The policy is compiled INTO the sudo binary (--enable-static-sudoers), so the # plugin directory must exist and be empty. A sudoers.so appearing here would # mean the static build silently reverted to the dlopen path, which this arm has # no loader for -- the one packaging regression worth catching. check_eq sudo/static-sudoers "0" "$(ls -A /usr/lib/sudo 2>/dev/null | wc -l | tr -d ' ')" # ---- the sudoers language -------------------------------------------------- # sudo -V prints its version and full configure summary correctly, but exits 1, # because the same failed sudoers plugin init that blocks running a command also # taints the version path. Split the two: the version STRING is a real # capability and is asserted outright, while the exit status is attributed to # the umask blocker so it flips green with it. (sudo -h, by contrast, exits 0 -- # it never reaches the plugin.) check_out sudo/version "Sudo version 1.9.17p2" sh -c "sudo -V 2>&1 || true" check_out sudo/usage "execute a command as another user" sudo -h ver=$(sudo -V >/dev/null 2>&1; echo "rc=$?") case "$ver" in *rc=0*) bst_ok sudo/version-rc ;; *) bst_skip sudo/version-rc \ "sudo -V exits 1: sudoers plugin init fails on the unwired umask(2) (see sudo/run-command)" ;; esac check_out sudo/visudo-good "parsed OK" visudo -c -f "$T/good" check_rc sudo/visudo-badrc 1 visudo -c -f "$T/bad" check_out sudo/visudo-baddiag "syntax error" sh -c "visudo -c -f '$T/bad' 2>&1 || true" # The image's own /etc/sudoers, which is FUSE-backed and therefore carries # whatever ownership the image was staged with. Root-owned in a real deployed # rootfs (the tar ships it 0/0), so it parses; a locally-staged tree can present # it as owned by the staging user, which sudo correctly refuses. Report that # case as an attributed skip rather than a failure -- it is the staging path, # not the port. out=$(visudo -c 2>&1) case "$out" in *"parsed OK"*) bst_ok sudo/visudo-etc ;; *"wrong owner"*|*"should be 0"*) bst_skip sudo/visudo-etc "staged /etc/sudoers not root-owned (local-verify staging artifact; ships 0/0)" ;; *) bst_notok sudo/visudo-etc "$(_bst_1line "$out")" ;; esac check_out sudo/cvt-json '"env_reset": true' cvtsudoers -f json "$T/good" check_out sudo/cvt-ldif "objectClass: sudoRole" cvtsudoers -f ldif -b ou=SUDOers,dc=x "$T/good" check_out sudo/cvt-normalize "root ALL = (ALL : ALL) ALL" cvtsudoers -f sudoers "$T/good" # The filter drops the %wheel rule and keeps root's -- a real policy query, not # a reformat, so it proves the matcher and not just the printer. flt=$(cvtsudoers -f sudoers -m user=root "$T/good" 2>&1) case "$flt" in *"root ALL = (ALL : ALL) ALL"*) case "$flt" in *wheel*) bst_notok sudo/cvt-filter "%wheel rule survived a user=root filter" ;; *) bst_ok sudo/cvt-filter ;; esac ;; *) bst_notok sudo/cvt-filter "$(_bst_1line "$flt")" ;; esac check_out sudo/sudoreplay-list "/var/log/sudo-io" sh -c "sudoreplay -l 2>&1 || true" check_out sudo/logsrvd-usage "sudo log server" sh -c "sudo_logsrvd -h 2>&1 || true" check_out sudo/sendlog-usage "usage: sudo_sendlog" sh -c "sudo_sendlog -h 2>&1 || true" # ---- running a command (the umask blocker) --------------------------------- run=$(sudo -n true 2>&1; echo "rc=$?") case "$run" in *"value too large"*|*"error initializing audit plugin"*) bst_skip sudo/run-command \ "umask(2) unwired on wasm32-hwjs: umask() returns -ENOSYS (037777777732), sudoers rejects it and policy plugin init fails (BRINTOS-205)" ;; *rc=0*) bst_ok sudo/run-command ;; *) bst_notok sudo/run-command "$(_bst_1line "$run")" ;; esac rm -rf "$T"