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
|
|
@ -361,6 +361,82 @@ fs_readonly(uint dev)
|
|||
return SCHFS_IS_SNAP_DEV(dev) || (FAT_IS_DEV(dev) && fat_readonly(dev));
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Permissions
|
||||
// ===========================================================================
|
||||
|
||||
// Is `gid` the caller's group, effective or supplementary? `real` asks about
|
||||
// the real group instead of the effective one -- access(2) does.
|
||||
int
|
||||
in_group(uint gid, int real)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
|
||||
if (p == 0)
|
||||
return 1;
|
||||
if (gid == (real ? p->gid : p->egid))
|
||||
return 1;
|
||||
for (int i = 0; i < p->ngroups; i++)
|
||||
if (p->groups[i] == gid)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The permission bits of an inode, 07777, as stat reports them. A filesystem
|
||||
// that keeps real ones (SchFS) supplies them; the rest are made up from the
|
||||
// type, which is what stat has always done. A mode that was recorded as "no
|
||||
// bits" is 01000000, which masks to zero here.
|
||||
uint
|
||||
inode_mode_bits(struct inode *ip)
|
||||
{
|
||||
if (ip->mode)
|
||||
return ip->mode & 07777;
|
||||
switch (ip->type) {
|
||||
case T_DIR: return FAT_IS_DEV(ip->dev) ? 0777 : 0755;
|
||||
case T_DEV: return 0666;
|
||||
case T_SYMLINK: return 0777;
|
||||
case T_SOCK: return 0755;
|
||||
}
|
||||
// A volume that cannot say who owns anything is everyone's: FAT on a USB
|
||||
// stick was usable by whoever was logged in, and still is.
|
||||
if (FAT_IS_DEV(ip->dev))
|
||||
return 0777;
|
||||
return (ip->rwmode & 2) ? 0755 : 0555;
|
||||
}
|
||||
|
||||
// May the caller do `mask` (4 read, 2 write, 1 execute or search) to this
|
||||
// inode? 0, or -EACCES. The caller holds the inode lock or knows it will not
|
||||
// change under it.
|
||||
//
|
||||
// Root may read and write anything, and may execute a file only if somebody
|
||||
// can -- the same rule Linux applies, so that a data file does not become a
|
||||
// program because root asked.
|
||||
int
|
||||
inode_permission(struct inode *ip, int mask, int real)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
uint m = inode_mode_bits(ip);
|
||||
uint who = p ? (real ? p->uid : p->euid) : 0;
|
||||
|
||||
if (p == 0 || who == 0) {
|
||||
if ((mask & 1) && ip->type != T_DIR && !(m & 0111))
|
||||
return -EACCES;
|
||||
return 0;
|
||||
}
|
||||
if (who == ip->uid)
|
||||
m >>= 6;
|
||||
else if (in_group(ip->gid, real))
|
||||
m >>= 3;
|
||||
return ((m & 7) & (uint)mask) == (uint)mask ? 0 : -EACCES;
|
||||
}
|
||||
|
||||
// Superuser-only operations: mount, reboot, settimeofday and the rest.
|
||||
int
|
||||
capable(void)
|
||||
{
|
||||
return myproc() == 0 || myproc()->euid == 0;
|
||||
}
|
||||
|
||||
static const struct fs_ops *
|
||||
ops_for(uint dev)
|
||||
{
|
||||
|
|
@ -626,6 +702,7 @@ ilock(struct inode *ip)
|
|||
|
||||
if (ip->valid == 0) {
|
||||
ip->mode = 0;
|
||||
ip->uid = ip->gid = 0;
|
||||
ip->ops->iload(ip);
|
||||
}
|
||||
}
|
||||
|
|
@ -1970,6 +2047,15 @@ namex_from(struct inode *start, char *path, int nameiparent, char *name,
|
|||
*err = ENOTDIR;
|
||||
goto fail;
|
||||
}
|
||||
// Searching a directory takes its execute bit. The directory that holds
|
||||
// the final name is searched too, even when the caller only wants its
|
||||
// inode: nothing under a directory you cannot enter is yours to name.
|
||||
if (inode_permission(ip, 1, 0) < 0) {
|
||||
iunlockput(ip);
|
||||
if (err)
|
||||
*err = EACCES;
|
||||
goto fail;
|
||||
}
|
||||
if (nameiparent && *spath == '\0') {
|
||||
iunlock(ip);
|
||||
goto out;
|
||||
|
|
|
|||
Loading…
Reference in a new issue