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

@ -9,7 +9,7 @@
// construction — there is only one implementation, exercised on the host where
// a mistake costs a second instead of a boot.
//
// mkschfs out.img --sysroot DIR [--size BLOCKS] [--label NAME]
// mkschfs out.img --sysroot DIR [--size BLOCKS] [--label NAME] [--owners FILE]
//
// Two behaviours worth knowing about:
//
@ -289,6 +289,63 @@ die(const char *what, int r)
exit(1);
}
/* Who owns what. The host's owners mean nothing inside the image -- every file
came from whoever ran the build -- so everything is root's unless a rule in
the --owners file says otherwise. One rule per line, `path uid gid [mode]`,
with the path relative to the sysroot ("/home/schos"). uid and gid apply to
the path and to everything below it; a mode, in octal, to that path alone.
Later rules win. */
struct owner_rule { char *path; uint32_t uid, gid; int has_mode; uint32_t mode; };
static struct owner_rule *owner_rules;
static int nowner_rules;
static const char *cur_rel; /* the entry being added, set by add_entry */
static void
load_owners(const char *file)
{
FILE *f = fopen(file, "r");
char line[1024];
if (!f) { perror(file); exit(1); }
while (fgets(line, sizeof line, f)) {
char path[900];
unsigned uid, gid, mode;
int n;
if (line[0] == '#' || line[0] == '\n')
continue;
n = sscanf(line, "%899s %u %u %o", path, &uid, &gid, &mode);
if (n < 3) { fprintf(stderr, "mkschfs: bad owners line: %s", line); exit(1); }
owner_rules = realloc(owner_rules, (size_t)(nowner_rules + 1) * sizeof *owner_rules);
owner_rules[nowner_rules].path = strdup(path);
owner_rules[nowner_rules].uid = uid;
owner_rules[nowner_rules].gid = gid;
owner_rules[nowner_rules].has_mode = n == 4;
owner_rules[nowner_rules].mode = mode;
nowner_rules++;
}
fclose(f);
}
static void
apply_owners(struct schfs_inode *in)
{
if (!cur_rel)
return;
for (int i = 0; i < nowner_rules; i++) {
const char *r = owner_rules[i].path;
size_t n;
if (*r == '/')
r++; /* cur_rel is relative: "home/schos" */
n = strlen(r);
int exact = strcmp(cur_rel, r) == 0;
if (!exact && !(strncmp(cur_rel, r, n) == 0 && cur_rel[n] == '/'))
continue;
in->uid = owner_rules[i].uid;
in->gid = owner_rules[i].gid;
if (exact && owner_rules[i].has_mode)
in->mode = owner_rules[i].mode;
}
}
static void
inode_init(struct schfs_inode *in, const struct stat *st, uint16_t type,
uint64_t parent)
@ -301,6 +358,7 @@ inode_init(struct schfs_inode *in, const struct stat *st, uint16_t type,
: (type == SCHFS_T_DIR ? 0755u : 0644u);
in->uid = 0;
in->gid = 0;
apply_owners(in);
if (st) {
// All three come from mtime, deliberately.
//
@ -537,6 +595,7 @@ add_entry(uint64_t dirino, const char *hostdir, const char *name)
// lstat, not stat: a symlink must be stored as a symlink rather than
// silently turned into a second copy of whatever it points at.
if (lstat(path, &st) != 0) { perror(path); exit(1); }
cur_rel = strlen(path) >= sysroot_prefix ? path + sysroot_prefix : path;
if (strlen(name) > SCHFS_NAME_MAX) {
fprintf(stderr, "mkschfs: name too long (max %d): %s\n",
@ -631,6 +690,8 @@ main(int argc, char **argv)
nblocks = strtoull(argv[++i], NULL, 0);
else if (strcmp(argv[i], "--label") == 0 && i + 1 < argc)
label = argv[++i];
else if (strcmp(argv[i], "--owners") == 0 && i + 1 < argc)
load_owners(argv[++i]);
else if (strcmp(argv[i], "--snapshot") == 0 && i + 1 < argc)
snapname = argv[++i];
else if (strcmp(argv[i], "--exclude") == 0 && i + 1 < argc) {