@ai-ecoverse/slicc-kernel 1.64.0 → 1.64.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/kernel-worker.js +58 -11
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +21 -2
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +58 -11
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +21 -2
- package/dist/process-worker.js.map +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -561,6 +561,7 @@ With `users: 'enforce'` the kernel checks every file call that reaches it agains
|
|
|
561
561
|
- **`access(2)`:** `faccessat` (bash's `[ -r ]`, `test -w`) answers from the same checks, with the real uid and gid, or the effective ones with `AT_EACCESS`, for Emscripten programs, and for WASI programs through `slicc_fs.path_access` (see [File modes and the umask](#file-modes-and-the-umask)).
|
|
562
562
|
- **Exec:** running a program by path needs `x` on it, and `r` too when it's a script (`#!` or a `binfmt` suffix); a WASI or Emscripten binary needs only `x`. Root needs an `x` bit as well. Commands found by name, and `/usr/bin` and `/bin`, are always executable; a user's own commands in `~/.local/share/pnpm/bin` are reached through the user's home, so another user gets `EACCES` there, as for any file. A refused exec is `EACCES` (126 in bash).
|
|
563
563
|
- **The layout:** with `users: 'report'` or `'enforce'`, the kernel gives its own paths an owner and a mode when they have no owner record yet: on a fresh root, and once on a root from before owners. These are `/` `0755`, `/root` `0700`, `/home` `0755`, `/etc` `0755`, `/etc/passwd` and `/etc/group` `0644`, `/usr` and `/usr/local` `0755`, `/node_modules` (or the `modules` option) and the system `PNPM_HOME` `0755`, all root's. A path that has a record keeps it, so an admin's `chmod 711 /home` or `chown cone /node_modules` lasts across restarts; a mounted path is left to its mount, and a path below a symlinked directory is stamped where it really is. Only a root that records owners is stamped: the OPFS root with its sidecar, which the page and the Node entry (in memory) both use; a custom root file system without owner records is left as it is. The directory for files removed while open, `/.slicc-unlinked`, is made root's `0700`. `/tmp` is the exception: it is set to `1777` at every start. Emscripten programs see `/` at `0755`, `/dev` at `0755`, `null`, `zero`, `full`, `random`, `urandom` and `tty` at `0666`, and `/dev/shm` at `1777`. With `users: 'off'` nothing is stamped. Paths the embedder makes (bios's `/opt` and `/var/lib/slicc`) are the embedder's to set, and `/etc/sudoers` comes with `sudo`.
|
|
564
|
+
- **Terminals:** a terminal from `openTerminal` belongs to its session's user, with group `tty` when `/etc/group` has one (the user's own group otherwise), mode `0620`; a pseudo-terminal's slave `/dev/pts/N` belongs to the uid and gid that opened `/dev/ptmx`, mode `0620`. Opening one by name (`/dev/ttyN`, `/dev/pts/N`, from every runtime) needs both `r` and `w`, so only its owner and root open it: the kernel's terminal descriptor always reads and writes, so the group's `w` bit (`write`, `wall` on Linux) does not let a group member open it. `/dev/tty` is the opener's own controlling terminal and is not checked, and descriptors a process inherited (across `sudo` or a `setresuid` too) keep working. Emscripten programs' `stat` of `/dev/ttyN` and `/dev/pts/N` shows the owner and mode. With `users: 'off'` terminals have no owner, as before.
|
|
564
565
|
- **A client's `fs`:** checked as the client's user; a page's client (root) passes.
|
|
565
566
|
|
|
566
567
|
With `users: 'enforce'` not yet the default, no release turns it on before `sudo` exists.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -1484,6 +1484,11 @@ var Created = class {
|
|
|
1484
1484
|
function deny(g, op, path, errno = "EACCES") {
|
|
1485
1485
|
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
|
|
1486
1486
|
}
|
|
1487
|
+
var TTY_MODE = 400;
|
|
1488
|
+
function ttyOpenable(g, tty) {
|
|
1489
|
+
if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
|
|
1490
|
+
deny(g, "open", tty.name ?? "/dev/tty");
|
|
1491
|
+
}
|
|
1487
1492
|
async function need(g, op, path, want, options = {}, shown = path) {
|
|
1488
1493
|
const walked = await access(g.fs, g.cred, path, want, options);
|
|
1489
1494
|
if (walked.denied !== void 0) deny(g, op, shown);
|
|
@@ -3196,10 +3201,18 @@ var UserDb = class {
|
|
|
3196
3201
|
};
|
|
3197
3202
|
}
|
|
3198
3203
|
async lookup(user) {
|
|
3204
|
+
return (await this.lookupTerminal(user))?.account;
|
|
3205
|
+
}
|
|
3206
|
+
async ttyGroup() {
|
|
3207
|
+
return parsed(parseGroup(await this.read(GROUP, DEFAULT_GROUP))).find((g) => g.name === "tty")?.gid;
|
|
3208
|
+
}
|
|
3209
|
+
async lookupTerminal(user) {
|
|
3199
3210
|
const { passwd, group } = await this.tables();
|
|
3200
3211
|
const key = typeof user === "number" ? "uid" : "name";
|
|
3201
3212
|
const row = parsed(passwd).find((r) => r[key] === user);
|
|
3202
|
-
|
|
3213
|
+
const groups = parsed(group);
|
|
3214
|
+
const tty = groups.find((g) => g.name === "tty")?.gid;
|
|
3215
|
+
return row && { account: this.account(row, groups), tty };
|
|
3203
3216
|
}
|
|
3204
3217
|
async list() {
|
|
3205
3218
|
const { passwd, group } = await this.tables();
|
|
@@ -9816,6 +9829,7 @@ var KernelTty = class {
|
|
|
9816
9829
|
waiters = [];
|
|
9817
9830
|
hungUp = false;
|
|
9818
9831
|
name;
|
|
9832
|
+
owner;
|
|
9819
9833
|
onRead;
|
|
9820
9834
|
readers = 0;
|
|
9821
9835
|
screen;
|
|
@@ -10159,10 +10173,11 @@ var PtyTable = class {
|
|
|
10159
10173
|
this.signal = signal;
|
|
10160
10174
|
this.onRead = onRead;
|
|
10161
10175
|
}
|
|
10162
|
-
open() {
|
|
10176
|
+
open(owner) {
|
|
10163
10177
|
let index = 0;
|
|
10164
10178
|
while (this.pairs.has(index)) index += 1;
|
|
10165
10179
|
const pair = new PtyPair(index, this.signal, (p) => this.pairs.delete(p.index), this.onRead);
|
|
10180
|
+
if (owner) pair.slave.owner = { uid: owner.uid, gid: owner.gid, mode: TTY_MODE };
|
|
10166
10181
|
this.pairs.set(index, pair);
|
|
10167
10182
|
return { pair, master: pair.master() };
|
|
10168
10183
|
}
|
|
@@ -10175,6 +10190,10 @@ var PtyTable = class {
|
|
|
10175
10190
|
return [...this.pairs.keys()].sort((a, b) => a - b);
|
|
10176
10191
|
}
|
|
10177
10192
|
};
|
|
10193
|
+
function ptsNumber(path) {
|
|
10194
|
+
const m = /^\/dev\/pts\/(\d+)$/.exec(path);
|
|
10195
|
+
return m ? Number(m[1]) : void 0;
|
|
10196
|
+
}
|
|
10178
10197
|
var PTY_OPS = [
|
|
10179
10198
|
"pty-open",
|
|
10180
10199
|
"pty-slave-open",
|
|
@@ -10202,11 +10221,12 @@ function ptySyscall(req, ctx) {
|
|
|
10202
10221
|
switch (req.op) {
|
|
10203
10222
|
case "pty-open": {
|
|
10204
10223
|
if (!ctx.ptys) throw new KernelError("ENOENT");
|
|
10205
|
-
return json4(ctx.fds.install(ctx.ptys.open().master, 3));
|
|
10224
|
+
return json4(ctx.fds.install(ctx.ptys.open(ctx.owner).master, 3));
|
|
10206
10225
|
}
|
|
10207
10226
|
case "pty-slave-open": {
|
|
10208
10227
|
if (!ctx.ptys) throw new KernelError("ENOENT");
|
|
10209
10228
|
const pair = ctx.ptys.get(req.n);
|
|
10229
|
+
ttyOpenable(ctx.guard, pair.slave);
|
|
10210
10230
|
const fd = ctx.fds.install(pair.openSlave(), 3);
|
|
10211
10231
|
if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
|
|
10212
10232
|
return json4(fd);
|
|
@@ -11022,6 +11042,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
11022
11042
|
"fd-promote",
|
|
11023
11043
|
"fd-open-tty",
|
|
11024
11044
|
"fd-tty-names",
|
|
11045
|
+
"fd-tty-owner",
|
|
11025
11046
|
"tty-get",
|
|
11026
11047
|
"tty-set",
|
|
11027
11048
|
"tty-winsz",
|
|
@@ -11223,7 +11244,9 @@ var WasmProcess = class {
|
|
|
11223
11244
|
}
|
|
11224
11245
|
if (isPtySyscall(req)) {
|
|
11225
11246
|
const { ptys, jobs } = this.options;
|
|
11226
|
-
|
|
11247
|
+
const owner = this.options.owners ? this.ttyOwner() : void 0;
|
|
11248
|
+
const guard2 = this.guarded();
|
|
11249
|
+
return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner });
|
|
11227
11250
|
}
|
|
11228
11251
|
return await this.procSyscall(req, cancelled);
|
|
11229
11252
|
} catch (e) {
|
|
@@ -11355,6 +11378,25 @@ var WasmProcess = class {
|
|
|
11355
11378
|
const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
|
|
11356
11379
|
await openable(g, "open", req.path, want, req.create === true);
|
|
11357
11380
|
}
|
|
11381
|
+
openTty(name2) {
|
|
11382
|
+
const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
|
|
11383
|
+
if (!tty) throw new KernelError("ENXIO");
|
|
11384
|
+
if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
|
|
11385
|
+
return this.fds.install(tty.file(), 3);
|
|
11386
|
+
}
|
|
11387
|
+
guarded() {
|
|
11388
|
+
const { guard: guard2 } = this.options;
|
|
11389
|
+
return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
|
|
11390
|
+
}
|
|
11391
|
+
ttyOwner() {
|
|
11392
|
+
return { uid: this.cred.euid, gid: this.cred.egid, mode: TTY_MODE };
|
|
11393
|
+
}
|
|
11394
|
+
terminalByName(name2) {
|
|
11395
|
+
const n = ptsNumber(name2);
|
|
11396
|
+
const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) : this.options.ptys?.get(n).slave;
|
|
11397
|
+
if (!tty) throw new KernelError("ENOENT");
|
|
11398
|
+
return tty;
|
|
11399
|
+
}
|
|
11358
11400
|
newOwner() {
|
|
11359
11401
|
if (!this.options.owners) return void 0;
|
|
11360
11402
|
return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
|
|
@@ -11458,13 +11500,12 @@ var WasmProcess = class {
|
|
|
11458
11500
|
if (!req.keep) await Promise.resolve(this.fds.close(req.from));
|
|
11459
11501
|
}
|
|
11460
11502
|
return { ok: true, kind: "void" };
|
|
11461
|
-
case "fd-open-tty":
|
|
11462
|
-
|
|
11463
|
-
if (!tty) throw new KernelError("ENXIO");
|
|
11464
|
-
return { ok: true, kind: "json", json: this.fds.install(tty.file(), 3) };
|
|
11465
|
-
}
|
|
11503
|
+
case "fd-open-tty":
|
|
11504
|
+
return { ok: true, kind: "json", json: this.openTty(req.name) };
|
|
11466
11505
|
case "fd-tty-names":
|
|
11467
11506
|
return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
|
|
11507
|
+
case "fd-tty-owner":
|
|
11508
|
+
return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
|
|
11468
11509
|
case "fd-select": {
|
|
11469
11510
|
const { read, write, timeoutMs } = req;
|
|
11470
11511
|
if (this.owed && timeoutMs !== 0) {
|
|
@@ -15086,7 +15127,12 @@ var Launcher = class {
|
|
|
15086
15127
|
}
|
|
15087
15128
|
async account(user) {
|
|
15088
15129
|
if (typeof user === "object") return user;
|
|
15089
|
-
|
|
15130
|
+
return (await this.terminalAccount(user)).account;
|
|
15131
|
+
}
|
|
15132
|
+
async terminalAccount(user) {
|
|
15133
|
+
if (typeof user === "object")
|
|
15134
|
+
return { account: user, tty: this.owners ? await this.users.ttyGroup() : void 0 };
|
|
15135
|
+
const found = await this.users.lookupTerminal(user ?? 0);
|
|
15090
15136
|
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
15091
15137
|
return found;
|
|
15092
15138
|
}
|
|
@@ -15108,7 +15154,7 @@ var Launcher = class {
|
|
|
15108
15154
|
return { cwd, file, planned };
|
|
15109
15155
|
}
|
|
15110
15156
|
async openTerminal(argv, options) {
|
|
15111
|
-
const account = await this.
|
|
15157
|
+
const { account, tty: ttyGroup } = await this.terminalAccount(options.user);
|
|
15112
15158
|
const { cwd, file, planned } = await this.starting(argv, options.cwd, account);
|
|
15113
15159
|
if (!planned) throw new Error(`${file}: command not found`);
|
|
15114
15160
|
let leader = 0;
|
|
@@ -15118,6 +15164,7 @@ var Launcher = class {
|
|
|
15118
15164
|
);
|
|
15119
15165
|
tty.onRead = () => this.settling.reading(this.jobs.tcgetpgrp(tty, leader));
|
|
15120
15166
|
tty.name = `/dev/tty${++this.terminals}`;
|
|
15167
|
+
if (this.owners) tty.owner = { uid: account.uid, gid: ttyGroup ?? account.gid, mode: TTY_MODE };
|
|
15121
15168
|
tty.setSize(options.cols ?? 80, options.rows ?? 24);
|
|
15122
15169
|
const fds = new FdTable();
|
|
15123
15170
|
const stdio = tty.file();
|