SchOS/docs/architecture/users.md
Nexoniarz 020aa6113c 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
2026-10-26 13:16:07 +01:00

4.2 KiB
Raw Blame History

Users and permissions

SchOS has accounts, a login prompt, and file permissions that the kernel enforces. Until now every process was root and uid was bookkeeping; this is the part that makes it a multi-user system.

What you meet

  • login on tty1–tty5. etc/schos-getty takes the terminal back from whoever had it and runs login, which asks who is there and starts their shell; when the shell exits schinit starts the prompt again.
  • tty6 is a root shell with no login, on purpose: the way in when login itself is what is broken.
  • Two accounts in the image. root, and schos (uid 1000, home /home/schos, in wheel tty video input audio cdrom). Both have empty passwords, so login lets them in without asking — it is a live image. passwd sets one.
  • su and passwd (cmd/su, cmd/passwd) are setuid root and small programs of their own, not toybox applets: toybox is one binary under every name, and a setuid copy of it can be asked for any applet by naming it, as root. su to root takes membership in wheel, whatever the password.
  • doas (Alpine's, setuid) with permit nopass :wheel in /etc/doas.conf.

What the kernel enforces

open, create, unlink, rename, link the usual owner/group/other bits, w+x on the directory, and the sticky bit (/tmp)
path walk x on every directory searched
exec x on the file; set-user-id and set-group-id make the new image run as its owner or group, and the saved ids follow
chmod, chown, utimensat owner or root; a non-root chown drops the set-id bits
access / faccessat by the real ids (AT_EACCESS for the effective ones)
kill, tkill, tgkill root, or a target whose real or saved uid is one of yours. The kernel's own signals (SIGPIPE, the terminal's) are nobody's to refuse
mount, umount, chroot, reboot, shutdown, setgroups, mknod of a device root
setuid family root may set anything; anyone else only to an id they already hold

Root reads and writes everything and executes a file only if some execute bit is set. A process's supplementary groups (setgroups) are consulted along with its effective group. Ownership is stored by SchFS (it always had the fields); FAT volumes belong to everyone, since they cannot say otherwise, and /proc and /sys are root's.

inode_permission() in kernel/fs/fs.c is the one check; the rest of this is call sites.

Devices

/dev is built by the kernel and each node has the owner, group and mode in the devnodes[] table of kernel/fs/devfs.c: null and zero are 0666, terminals root:tty 0660, fb0 root:video, input root:input, raw disks root:disk, pci, dbg and netdev root's alone. chmod and chown on a node are kept (login claims its terminal this way, and udev sets modes) in an override table applied whenever the inode is loaded. A new pseudo-terminal's slave belongs to whoever opened ptmx.

udev would set a node it has no rule for to 0600, which made /dev/null root-only, so etc/udev-schos.rules (installed as 90-schos.rules) names the kernel's devices with the answers the kernel gave.

The image

Everything in the image is owned by root: the build host's user means nothing inside it. mkschfs --owners FILE takes the exceptions, one path uid gid [mode] per line (tools/posix/mkaroot.sh writes it: /home/schos 1000 1000 0700, and the modes of /tmp, /var/tmp and /root). The setuid bits are set on su, passwd and doas in the root tree directly, because apk run as an ordinary user may have dropped them.

Not done

  • No PAM and no /etc/securetty. login is busybox's and checks /etc/shadow itself.
  • No capabilities. Root is euid == 0, all of it.
  • No per-user /run, no logind. X as a non-root user works (startx as schos, under QEMU: Window Maker up) by group permission alone — video, input and tty — and /etc/X11/Xwrapper.config lets anyone start it, because the wrapper's "console users" test looks for a Linux VT on a standard descriptor that xinit's server child does not keep.
  • /proc and /sys hide nothing from other users.
  • mprotect is still not enforced (kernel.md).