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
79 lines
4.2 KiB
Markdown
79 lines
4.2 KiB
Markdown
# 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](kernel.md)).
|