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:
parent
b3f72cd95e
commit
020aa6113c
34 changed files with 1342 additions and 120 deletions
79
docs/architecture/users.md
Normal file
79
docs/architecture/users.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# 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)).
|
||||
Loading…
Reference in a new issue