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
|
|
@ -94,6 +94,7 @@ Read in this order if you are new:
|
|||
| [fat.md](fat.md) | FAT12/16/32 with long names: USB sticks, SD cards, the ESP. |
|
||||
| [devices.md](devices.md) | Buses, classes, binding, and the vtables consumers talk to. |
|
||||
| [usb.md](usb.md) | xHCI, the USB core, and the three class drivers. |
|
||||
| [users.md](users.md) | Accounts, login, and the permissions the kernel enforces. |
|
||||
| [udev.md](udev.md) | eudev on SchOS: the device table, the `/sys` tree, uevents, datagram sockets. |
|
||||
| [graphics.md](graphics.md) | Framebuffer, `/dev/fb0`, text rendering, the console, PSOGM and GMMOS. |
|
||||
| [desktop.md](desktop.md) | Xorg on the framebuffer, Window Maker, and what they stand on. |
|
||||
|
|
|
|||
|
|
@ -75,4 +75,5 @@ through ACPI, and reach X as a mouse: pointer, click and tap.
|
|||
- **Touchpads are mice**: no two-finger scroll, no gestures, no palm rejection.
|
||||
- **No resolution change from inside X.** The mode is the console's; `schmode` changes the
|
||||
`vgpud` head only, and the loader's menu is where a real panel's mode is chosen.
|
||||
- **Not a security boundary.** X runs as root on a system that enforces no file permissions.
|
||||
- **Runs as an ordinary user.** `startx` as `schos` works by group permission alone
|
||||
([users.md](users.md)); X itself is still a single seat with no logind or seatd.
|
||||
|
|
|
|||
|
|
@ -435,9 +435,8 @@ allocation over 8 MB mapped straight into a neighbouring live mapping.
|
|||
- **No page cache.** `mmap` of a file *reads the range in* at map time, and
|
||||
writable `MAP_SHARED` of a file is refused outright rather than faked. Demand
|
||||
paging covers anonymous memory only.
|
||||
- **Permissions are tracked but not enforced.** uid/gid are maintained honestly
|
||||
so `getuid()`/`setuid()` stop lying, and file access does not consult them;
|
||||
`setpriority` is the one place the uids are checked.
|
||||
- **Root is `euid == 0` and nothing finer.** No capabilities; file access, `kill`, `exec`
|
||||
and the privileged calls are enforced as [users.md](users.md) says.
|
||||
- **`mprotect` is accepted and not enforced** (`sys_mprotect` returns 0). RELRO is not read-only,
|
||||
a `PROT_NONE` guard does not fault, and nothing has `NX` (`EFER.NXE` is never set), so every
|
||||
mapping is executable.
|
||||
|
|
|
|||
|
|
@ -102,9 +102,8 @@ tracked. Two rules, with the two errnos POSIX gives them:
|
|||
alone. Without it nice is not a courtesy but a way for any process to
|
||||
outrank the rest of the system by asking.
|
||||
|
||||
Both are inert today, because everything runs as root. They are written now
|
||||
because the alternative is growing a priority knob and a permission model in
|
||||
separate commits, and the window between the two is exactly the bug.
|
||||
Both bite now that there are users ([users.md](users.md)); root is exempt from
|
||||
both.
|
||||
|
||||
## Deliberately not a red-black tree
|
||||
|
||||
|
|
|
|||
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