Users and permissions: accounts, login, enforced file permissions, setuid, su, passwd, doas

The kernel now checks owner/group/other on open, create, unlink, rename, exec
and path search; honours set-user-id and set-group-id; restricts kill, mount,
reboot and mknod; keeps supplementary groups; and implements chown. devtmpfs
nodes carry owners and modes that chmod/chown keep. Terminals run login, the
image has root and schos, X starts as the ordinary user.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
Nexoniarz 2026-10-26 13:16:07 +01:00
commit 020aa6113c
34 changed files with 1342 additions and 120 deletions

View file

@ -91,6 +91,19 @@ rm -rf "$P/usr/share/hwdata"
esac
done
# su and passwd are SchOS's own and run setuid root (cmd/su, cmd/passwd): the
# busybox ones are not setuid here, and a setuid toybox is one binary under
# every name. passwd goes where PATH finds it before busybox's link.
mkdir -p "$P/usr/bin"
cp "$SYSROOT/bin/su" "$P/bin/su"
cp "$SYSROOT/bin/passwd" "$P/usr/bin/passwd"
rm -f "$P/bin/passwd"
chmod 4755 "$P/bin/su" "$P/usr/bin/passwd"
# udev's device permissions for the kernel's own nodes.
mkdir -p "$P/etc/udev/rules.d"
mv "$P/etc/udev-schos.rules" "$P/etc/udev/rules.d/90-schos.rules"
# SchOS's profile without the three lines Alpine's already sets, and sets
# better: PATH with sbin, HOME from passwd, TERM from the console job.
mkdir -p "$P/etc/profile.d"
@ -157,6 +170,15 @@ Section "ServerLayout"
EndSection
XEOF
# Who may start X. The wrapper's default is "console users", decided by whether
# a standard descriptor is a Linux VT, which xinit's server child does not
# keep. SchOS has one seat, and /dev's group permissions (video, input, tty)
# are what actually guard the hardware, so anyone who can open them may run X.
cat > "$P/etc/X11/Xwrapper.config" <<'XEOF'
allowed_users=anybody
needs_root_rights=no
XEOF
# bwrap, without the sandbox. glycin -- which loads every image gdk-pixbuf is
# asked for, so every GTK icon -- runs its loaders under bubblewrap and has no
# other way to start them. Real bwrap needs user and mount namespaces, which
@ -255,7 +277,7 @@ cp "$PKG/post-install" "$PKG/post-upgrade"
--info url:https://github.com/Nexoniarz/SchOS \
--info origin:schos-base \
--info "depends:alpine-baselayout busybox musl apk-tools zsh" \
--info "replaces:alpine-release alpine-baselayout zsh bubblewrap" \
--info "replaces:alpine-release alpine-baselayout zsh bubblewrap busybox" \
--files "$P" \
--script "post-install:$PKG/post-install" \
--script "post-upgrade:$PKG/post-upgrade" \
@ -333,6 +355,65 @@ fi
# other program checking /tmp would see.
chmod 1777 "$ROOT/tmp" "$ROOT/var/tmp"
# --- accounts -------------------------------------------------------------------
#
# Alpine's baselayout has root and the system accounts, with root's password
# locked. A live image needs a way in, so: root with no password, and an
# ordinary user, `schos`, who can become root with doas and su (they are in the
# wheel group) and uses the desktop. Both have empty passwords -- login lets
# them in without asking -- and `passwd` sets one.
#
# The numbers are Alpine's: wheel 10, tty 5, video 27, input 23, audio 18.
add_member() { # add_member GROUP USER
if grep -q "^$1:[^:]*:[^:]*:$" "$ROOT/etc/group"; then
sed -i "s/^\($1:[^:]*:[^:]*:\)$/\1$2/" "$ROOT/etc/group"
else
sed -i "s/^\($1:[^:]*:[^:]*:.*\)$/\1,$2/" "$ROOT/etc/group"
fi
}
sed -i 's/^root:[^:]*:/root::/' "$ROOT/etc/shadow"
grep -q '^schos:' "$ROOT/etc/passwd" ||
echo 'schos:x:1000:1000:SchOS user:/home/schos:/bin/zsh' >> "$ROOT/etc/passwd"
grep -q '^schos:' "$ROOT/etc/group" ||
echo 'schos:x:1000:' >> "$ROOT/etc/group"
grep -q '^schos:' "$ROOT/etc/shadow" ||
echo 'schos::0:0:::::' >> "$ROOT/etc/shadow"
for g in wheel tty video input audio cdrom; do
grep -q "^$g:.*[:,]schos\(,\|\$\)" "$ROOT/etc/group" || add_member $g schos
done
chmod 640 "$ROOT/etc/shadow"
# Who may become root with doas: the wheel group, without being asked again.
# The file has to be root's and not writable by anyone else, or doas refuses it.
cat > "$ROOT/etc/doas.conf" <<'DEOF'
# doas: see doas.conf(5). Members of wheel run anything as anyone.
permit nopass :wheel
DEOF
chmod 644 "$ROOT/etc/doas.conf"
# The user's home, with what startx wants there. root's are in /root.
mkdir -p "$ROOT/home/schos"
cp "$ROOT/root/.xinitrc" "$ROOT/root/.xserverrc" "$ROOT/home/schos/"
chmod 755 "$ROOT/home/schos/.xserverrc"
# zsh offers to write one on first login; /etc/zsh is where the settings are.
: > "$ROOT/home/schos/.zshrc"
chmod 700 "$ROOT/home/schos" "$ROOT/root"
# The setuid programs. Run as an ordinary user, apk may have dropped the bit,
# and nothing here should depend on whether it did.
for f in bin/su usr/bin/passwd usr/bin/doas; do
[ -f "$ROOT/$f" ] && chmod 4755 "$ROOT/$f"
done
# Who owns what inside the image: everything is root's -- the build host's
# user means nothing here -- except the user's home.
cat > "$OUT/owners" <<'OEOF'
/home/schos 1000 1000 0700
/root 0 0 0700
/tmp 0 0 1777
/var/tmp 0 0 1777
OEOF
# The repository goes in too, so the installed system can reinstall and
# upgrade schos-base, and Alpine's are listed for everything else.
mkdir -p "$ROOT/var/lib/schos"