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

@ -59,18 +59,27 @@ sys_kill(void)
// pid == 0 -> every process in the caller's own group
// pid == -1 -> every process we may signal (approximated as our group)
// pid < -1 -> the process group with id -pid
if (pid == 0 || pid == -1)
return sigsend_pgrp(myproc()->pgid, sig) < 0 ? -ESRCH : 0;
if (pid < -1)
return sigsend_pgrp(-pid, sig) < 0 ? -ESRCH : 0;
{
int r = 0;
if (pid == 0 || pid == -1)
r = sigsend_pgrp_user(myproc()->pgid, sig);
else if (pid < -1)
r = sigsend_pgrp_user(-pid, sig);
else
goto single;
return r == -2 ? -EPERM : r < 0 ? -ESRCH : 0;
}
single:
// sig == 0: existence check only
if (sig == 0)
return proc_exists(pid) ? 0 : -ESRCH;
// Raise the signal for real: pending bit + wakeup, handler delivery on
// the target's next syscall boundary, default action if unhandled.
return sigsend(pid, sig) < 0 ? -ESRCH : 0;
{
int r = sigsend_user(pid, sig);
return r == -2 ? -EPERM : r < 0 ? -ESRCH : 0;
}
}
// Defined with getrusage() further down; wait4() needs it too.
@ -208,6 +217,8 @@ sys_uptime(void)
addr_t sys_reboot(void)
{
if (!capable())
return -EPERM;
cprintf("System is rebooting...\n");
// A stick left mounted should come back readable, and marked clean.
schfs_ksync_all();
@ -223,6 +234,8 @@ addr_t sys_reboot(void)
addr_t sys_shutdown(void)
{
if (!capable())
return -EPERM;
cprintf("System is shutting down...\n");
// S5, through the firmware's own PM1 registers and its _PTS. The port
// writes after it are QEMU's, Bochs' and VirtualBox's fixed addresses,
@ -335,12 +348,19 @@ addr_t
sys_setreuid(void)
{
int ruid, euid;
struct proc *p = myproc();
argint(0, &ruid); argint(1, &euid);
if (myproc()->euid != 0)
return -EPERM;
if (ruid >= 0) myproc()->uid = (uint)ruid;
if (euid >= 0) myproc()->euid = (uint)euid;
myproc()->suid = myproc()->euid;
if (p->euid != 0) {
if (ruid >= 0 && (uint)ruid != p->uid && (uint)ruid != p->euid)
return -EPERM;
if (euid >= 0 && (uint)euid != p->uid && (uint)euid != p->euid &&
(uint)euid != p->suid)
return -EPERM;
}
if (ruid >= 0 || (euid >= 0 && (uint)euid != p->uid))
p->suid = euid >= 0 ? (uint)euid : p->euid;
if (ruid >= 0) p->uid = (uint)ruid;
if (euid >= 0) p->euid = (uint)euid;
return 0;
}
@ -348,12 +368,19 @@ addr_t
sys_setregid(void)
{
int rgid, egid;
struct proc *p = myproc();
argint(0, &rgid); argint(1, &egid);
if (myproc()->euid != 0)
return -EPERM;
if (rgid >= 0) myproc()->gid = (uint)rgid;
if (egid >= 0) myproc()->egid = (uint)egid;
myproc()->sgid = myproc()->egid;
if (p->euid != 0) {
if (rgid >= 0 && (uint)rgid != p->gid && (uint)rgid != p->egid)
return -EPERM;
if (egid >= 0 && (uint)egid != p->gid && (uint)egid != p->egid &&
(uint)egid != p->sgid)
return -EPERM;
}
if (rgid >= 0 || (egid >= 0 && (uint)egid != p->gid))
p->sgid = egid >= 0 ? (uint)egid : p->egid;
if (rgid >= 0) p->gid = (uint)rgid;
if (egid >= 0) p->egid = (uint)egid;
return 0;
}
@ -361,12 +388,19 @@ addr_t
sys_setresuid(void)
{
int r, e, sv;
struct proc *p = myproc();
argint(0, &r); argint(1, &e); argint(2, &sv);
if (myproc()->euid != 0)
return -EPERM;
if (r >= 0) myproc()->uid = (uint)r;
if (e >= 0) myproc()->euid = (uint)e;
if (sv >= 0) myproc()->suid = (uint)sv;
// Unprivileged, each id may be set only to one the process already has.
if (p->euid != 0) {
int ids[3] = { r, e, sv };
for (int i = 0; i < 3; i++)
if (ids[i] >= 0 && (uint)ids[i] != p->uid &&
(uint)ids[i] != p->euid && (uint)ids[i] != p->suid)
return -EPERM;
}
if (r >= 0) p->uid = (uint)r;
if (e >= 0) p->euid = (uint)e;
if (sv >= 0) p->suid = (uint)sv;
return 0;
}
@ -374,12 +408,18 @@ addr_t
sys_setresgid(void)
{
int r, e, sv;
struct proc *p = myproc();
argint(0, &r); argint(1, &e); argint(2, &sv);
if (myproc()->euid != 0)
return -EPERM;
if (r >= 0) myproc()->gid = (uint)r;
if (e >= 0) myproc()->egid = (uint)e;
if (sv >= 0) myproc()->sgid = (uint)sv;
if (p->euid != 0) {
int ids[3] = { r, e, sv };
for (int i = 0; i < 3; i++)
if (ids[i] >= 0 && (uint)ids[i] != p->gid &&
(uint)ids[i] != p->egid && (uint)ids[i] != p->sgid)
return -EPERM;
}
if (r >= 0) p->gid = (uint)r;
if (e >= 0) p->egid = (uint)e;
if (sv >= 0) p->sgid = (uint)sv;
return 0;
}
@ -412,10 +452,39 @@ sys_getresgid(void)
addr_t sys_getgroups(void)
{
int size;
addr_t list;
struct proc *p = myproc();
argint(0, &size);
return 0; // no supplementary groups
argaddr(1, &list);
if (size < 0)
return -EINVAL;
if (size == 0)
return p->ngroups; // how many there are
if (size < p->ngroups)
return -EINVAL;
if (p->ngroups && copy_to_user(list, p->groups, p->ngroups * sizeof(uint)) < 0)
return -EFAULT;
return p->ngroups;
}
addr_t sys_setgroups(void)
{
int size;
addr_t list;
uint tmp[NGROUPS_MAX];
struct proc *p = myproc();
argint(0, &size);
argaddr(1, &list);
if (p->euid != 0)
return -EPERM;
if (size < 0 || size > NGROUPS_MAX)
return -EINVAL;
if (size && copy_from_user(tmp, list, size * sizeof(uint)) < 0)
return -EFAULT;
p->ngroups = size;
memmove(p->groups, tmp, size * sizeof(uint));
return 0;
}
addr_t sys_setgroups(void) { return 0; }
// --- Process/scheduling extensions ---
@ -478,7 +547,10 @@ addr_t sys_tgkill(void)
// the calling thread.
if (tid <= 0) return -EINVAL;
if (sig == 0) return proc_exists(tid) ? 0 : -ESRCH;
return sigsend(tid, sig) < 0 ? -ESRCH : 0;
{
int r = sigsend_user(tid, sig);
return r == -2 ? -EPERM : r < 0 ? -ESRCH : 0;
}
}
addr_t sys_tkill(void)
@ -487,7 +559,10 @@ addr_t sys_tkill(void)
argint(0, &tid); argint(1, &sig);
if (tid <= 0) return 0;
if (sig == 0) return proc_exists(tid) ? 0 : -ESRCH;
return sigsend(tid, sig) < 0 ? -ESRCH : 0;
{
int r = sigsend_user(tid, sig);
return r == -2 ? -EPERM : r < 0 ? -ESRCH : 0;
}
}
addr_t sys_pause(void)