@amritk/nish-x86_64-linux 0.28.0 → 0.29.0
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/INSTALL.md +8 -5
- package/bin/nish +0 -0
- package/docs/AI.md +99 -24
- package/package.json +1 -1
- package/runtime/core/host.d.ts +2 -1
- package/runtime/core/host.mjs +4 -0
- package/runtime/nish.d.ts +127 -0
- package/runtime/nish.h +72 -3
- package/runtime/nish.mjs +18 -4
- package/runtime/runtime-net.c +386 -5
- package/runtime/runtime-os.c +59 -8
- package/runtime/runtime-parallel.c +60 -5
- package/runtime/runtime-sandbox.c +43 -34
- package/runtime/runtime-shm.c +541 -0
- package/runtime/runtime.c +827 -791
- package/scripts/build.sh +42 -12
- package/std/README.md +10 -3
- package/std/collections.ts +5 -5
- package/std/crypto/hmac.ts +305 -2
- package/std/crypto/jwt.ts +1661 -0
- package/std/json-typed.ts +34 -0
- package/std/json.ts +35 -6
- package/std/net/handler.ts +1348 -0
- package/std/net/http-client.ts +97 -16
- package/std/net/http-compress.ts +54 -12
- package/std/net/http1-server.ts +77 -23
- package/std/net/http1.ts +24 -4
- package/std/net/outbound.ts +409 -0
- package/std/net/prefork.ts +344 -20
- package/std/secret.ts +1 -1
- package/std/sync/ring.ts +587 -0
- package/std/sync/shm.ts +284 -0
- package/std/term-rate.ts +13 -1
- package/std/threads.ts +35 -3
- /package/runtime/{LICENSE-ryu → LICENSE-zmij} +0 -0
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* before any constructor in the executable, an `ffi` object or a shared
|
|
13
13
|
* library:
|
|
14
14
|
*
|
|
15
|
-
* nish_sandbox(caps, mode, read_dirs, write_dirs)
|
|
15
|
+
* nish_sandbox(caps, mode, read_dirs, write_dirs, exec_paths)
|
|
16
16
|
*
|
|
17
17
|
* `caps` is `main`'s capability mask (src/capabilities.ts numbers the bits),
|
|
18
18
|
* `mode` is 1 for `--sandbox` and 2 for `--sandbox=best-effort`. The two
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
* scopes, each a run of NUL-terminated absolute paths ended by an empty one,
|
|
21
21
|
* or null when the capability is granted whole or not at all; a list moves
|
|
22
22
|
* its capability's rights from the rule on `/` to one rule per directory.
|
|
23
|
+
* `exec_paths` is the programs a dependency's `process.spawn=<program>`
|
|
24
|
+
* grants name (WP40 §2a), and their interpreters, in the same shape: with
|
|
25
|
+
* it, `EXECUTE` leaves the rule on `/` and each program gets a rule of its own.
|
|
23
26
|
*
|
|
24
27
|
* What it does, in the order of the note's section 3, each step before the
|
|
25
28
|
* next because each needs the one before it:
|
|
@@ -169,27 +172,28 @@ static const uint16_t nish_sb_uring[] = {425, 426, 427}; /* io_uring_setup, _ent
|
|
|
169
172
|
/* Set by step 6 and read by `nish_sandbox_check`. */
|
|
170
173
|
static int nish_sb_applied = 0;
|
|
171
174
|
|
|
172
|
-
/* The errno
|
|
175
|
+
/* The errno a failure prints, as the line's parenthesized tail: its name, or
|
|
176
|
+
* its number for any other. */
|
|
173
177
|
static const char *nish_sb_errno_name(int e, char *buf) {
|
|
174
178
|
switch (e) {
|
|
175
|
-
case EPERM: return "EPERM";
|
|
176
|
-
case ENOSYS: return "ENOSYS";
|
|
177
|
-
case EOPNOTSUPP: return "EOPNOTSUPP";
|
|
178
|
-
case EINVAL: return "EINVAL";
|
|
179
|
-
case EACCES: return "EACCES";
|
|
180
|
-
case ENOENT: return "ENOENT";
|
|
181
|
-
case EFAULT: return "EFAULT";
|
|
182
|
-
case E2BIG: return "E2BIG";
|
|
183
|
-
case ENOMEM: return "ENOMEM";
|
|
184
|
-
case EBUSY: return "EBUSY";
|
|
179
|
+
case EPERM: return " (EPERM)";
|
|
180
|
+
case ENOSYS: return " (ENOSYS)";
|
|
181
|
+
case EOPNOTSUPP: return " (EOPNOTSUPP)";
|
|
182
|
+
case EINVAL: return " (EINVAL)";
|
|
183
|
+
case EACCES: return " (EACCES)";
|
|
184
|
+
case ENOENT: return " (ENOENT)";
|
|
185
|
+
case EFAULT: return " (EFAULT)";
|
|
186
|
+
case E2BIG: return " (E2BIG)";
|
|
187
|
+
case ENOMEM: return " (ENOMEM)";
|
|
188
|
+
case EBUSY: return " (EBUSY)";
|
|
185
189
|
}
|
|
186
190
|
char digits[12];
|
|
187
191
|
int n = 0;
|
|
188
192
|
unsigned u = (unsigned)e;
|
|
189
193
|
do digits[n++] = (char)('0' + u % 10); while ((u /= 10) != 0 && n < 11);
|
|
190
|
-
memcpy(buf, "errno ",
|
|
191
|
-
for (int i = 0; i < n; i++) buf[
|
|
192
|
-
buf
|
|
194
|
+
memcpy(buf, " (errno ", 8);
|
|
195
|
+
for (int i = 0; i < n; i++) buf[8 + i] = digits[n - 1 - i];
|
|
196
|
+
memcpy(buf + 8 + n, ")", 2);
|
|
193
197
|
return buf;
|
|
194
198
|
}
|
|
195
199
|
|
|
@@ -205,12 +209,7 @@ static void nish_sb_say(const char *a, const char *b, const char *c) {
|
|
|
205
209
|
* named after `what`. */
|
|
206
210
|
static void nish_sb_fail_at(const char *what, const char *path) {
|
|
207
211
|
char buf[24];
|
|
208
|
-
|
|
209
|
-
const char *name = nish_sb_errno_name(errno, buf);
|
|
210
|
-
size_t len = strlen(name);
|
|
211
|
-
memcpy(tail + 2, name, len);
|
|
212
|
-
memcpy(tail + 2 + len, ")", 2);
|
|
213
|
-
nish_sb_say(what, path, tail);
|
|
212
|
+
nish_sb_say(what, path, nish_sb_errno_name(errno, buf));
|
|
214
213
|
_exit(126);
|
|
215
214
|
}
|
|
216
215
|
|
|
@@ -279,15 +278,19 @@ static size_t nish_sb_name(char *line, size_t at, const char *name) {
|
|
|
279
278
|
return at + len;
|
|
280
279
|
}
|
|
281
280
|
|
|
282
|
-
/* One path_beneath rule granting `rights` beneath `path
|
|
281
|
+
/* One path_beneath rule granting `rights` beneath `path`, opened with
|
|
282
|
+
* `oflag` added: O_DIRECTORY for a scope directory, which refuses anything
|
|
283
|
+
* else, and 0 for a program the exec allowlist names, which O_PATH opens as
|
|
284
|
+
* a file. The failure lines name a directory either way, a program's too,
|
|
285
|
+
* because a second wording costs this unit's budget more than it says. A rule granting
|
|
283
286
|
* nothing is refused (ENOMSG), and nothing is what it would add, so it is
|
|
284
287
|
* left out. A scope directory gone since the build refuses strict; under
|
|
285
288
|
* best-effort it is named and its rule left out, which withholds the right
|
|
286
289
|
* there too, so the program loses the directory rather than gaining more. */
|
|
287
|
-
static void nish_sb_rule(int ruleset, const char *path, uint64_t rights, int32_t mode) {
|
|
290
|
+
static void nish_sb_rule(int ruleset, const char *path, uint64_t rights, int32_t mode, int oflag) {
|
|
288
291
|
struct nish_ll_path_beneath rule = {rights, -1};
|
|
289
292
|
if (!rights) return;
|
|
290
|
-
rule.parent_fd = open(path, O_PATH | O_CLOEXEC |
|
|
293
|
+
rule.parent_fd = open(path, O_PATH | O_CLOEXEC | oflag);
|
|
291
294
|
if (rule.parent_fd < 0) {
|
|
292
295
|
if (mode != 2) nish_sb_fail_at("cannot open the directory ", path);
|
|
293
296
|
nish_sb_say("the scope directory ", path, " cannot be opened, so the program gets no access beneath it");
|
|
@@ -297,9 +300,10 @@ static void nish_sb_rule(int ruleset, const char *path, uint64_t rights, int32_t
|
|
|
297
300
|
close(rule.parent_fd);
|
|
298
301
|
}
|
|
299
302
|
|
|
300
|
-
/* One rule per
|
|
301
|
-
|
|
302
|
-
|
|
303
|
+
/* One rule per path of a scope list, each granting `rights` and opened with
|
|
304
|
+
* `oflag` as `nish_sb_rule` says. */
|
|
305
|
+
static void nish_sb_scope(int ruleset, const char *dirs, uint64_t rights, int32_t mode, int oflag) {
|
|
306
|
+
for (; dirs && *dirs; dirs += strlen(dirs) + 1) nish_sb_rule(ruleset, dirs, rights, mode, oflag);
|
|
303
307
|
}
|
|
304
308
|
|
|
305
309
|
/* Step 4. Every right the ABI knows is handled, and one path_beneath rule on
|
|
@@ -307,7 +311,7 @@ static void nish_sb_scope(int ruleset, const char *dirs, uint64_t rights, int32_
|
|
|
307
311
|
* everywhere or nowhere; a scoped capability's rights are granted beneath its
|
|
308
312
|
* directories alone. Returns with nothing done when there is no Landlock and
|
|
309
313
|
* the mode is best-effort. */
|
|
310
|
-
static void nish_sb_landlock(int32_t caps, int32_t mode, const char *read_dirs, const char *write_dirs) {
|
|
314
|
+
static void nish_sb_landlock(int32_t caps, int32_t mode, const char *read_dirs, const char *write_dirs, const char *exec_paths) {
|
|
311
315
|
int abi = nish_sb_landlock_abi();
|
|
312
316
|
if (abi < 0) {
|
|
313
317
|
/* EPERM is a container runtime's own seccomp profile refusing the call;
|
|
@@ -331,6 +335,9 @@ static void nish_sb_landlock(int32_t caps, int32_t mode, const char *read_dirs,
|
|
|
331
335
|
if (read_dirs) whole &= ~CAP_BIT_FS_READ;
|
|
332
336
|
if (write_dirs) whole &= ~CAP_BIT_FS_WRITE;
|
|
333
337
|
uint64_t granted = nish_sb_granted(whole);
|
|
338
|
+
/* An exec allowlist (a dependency's process.spawn grant, WP40 §2a) keeps
|
|
339
|
+
* EXECUTE off `/` and grants it on each listed file alone. */
|
|
340
|
+
if (exec_paths) granted &= ~LL_EXECUTE;
|
|
334
341
|
uint64_t lost = ~granted & ~known & (LL_TRUNCATE | LL_IOCTL_DEV);
|
|
335
342
|
int unscoped = abi < 6;
|
|
336
343
|
/* Strict runs without IOCTL_DEV and without the ABI 6 scopes: a terminal
|
|
@@ -352,9 +359,10 @@ static void nish_sb_landlock(int32_t caps, int32_t mode, const char *read_dirs,
|
|
|
352
359
|
struct nish_ll_ruleset attr = {known, 0, abi >= 6 ? LL_SCOPE_ALL : 0};
|
|
353
360
|
int ruleset = (int)syscall(LL_CREATE, &attr, sizeof attr, 0);
|
|
354
361
|
if (ruleset < 0) nish_sb_fail("cannot create the Landlock ruleset");
|
|
355
|
-
nish_sb_rule(ruleset, "/", granted & known, 1);
|
|
356
|
-
if (read_dirs && (caps & CAP_BIT_FS_READ)) nish_sb_scope(ruleset, read_dirs, nish_sb_granted(CAP_BIT_FS_READ) & known, mode);
|
|
357
|
-
if (write_dirs && (caps & CAP_BIT_FS_WRITE)) nish_sb_scope(ruleset, write_dirs, nish_sb_granted(CAP_BIT_FS_WRITE) & known, mode);
|
|
362
|
+
nish_sb_rule(ruleset, "/", granted & known, 1, O_DIRECTORY);
|
|
363
|
+
if (read_dirs && (caps & CAP_BIT_FS_READ)) nish_sb_scope(ruleset, read_dirs, nish_sb_granted(CAP_BIT_FS_READ) & known, mode, O_DIRECTORY);
|
|
364
|
+
if (write_dirs && (caps & CAP_BIT_FS_WRITE)) nish_sb_scope(ruleset, write_dirs, nish_sb_granted(CAP_BIT_FS_WRITE) & known, mode, O_DIRECTORY);
|
|
365
|
+
if (caps & CAP_BIT_SPAWN) nish_sb_scope(ruleset, exec_paths, LL_EXECUTE, mode, 0);
|
|
358
366
|
if (syscall(LL_RESTRICT, ruleset, 0) != 0) nish_sb_fail("cannot apply the Landlock ruleset");
|
|
359
367
|
close(ruleset);
|
|
360
368
|
}
|
|
@@ -428,11 +436,11 @@ static void nish_sb_seccomp(int32_t caps) {
|
|
|
428
436
|
if (loaded != 0) nish_sb_fail("cannot load the seccomp filter");
|
|
429
437
|
}
|
|
430
438
|
|
|
431
|
-
void nish_sandbox(int32_t caps, int32_t mode, const char *read_dirs, const char *write_dirs) {
|
|
439
|
+
void nish_sandbox(int32_t caps, int32_t mode, const char *read_dirs, const char *write_dirs, const char *exec_paths) {
|
|
432
440
|
nish_cpu_prime();
|
|
433
441
|
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) nish_sb_fail("cannot set no_new_privs");
|
|
434
442
|
nish_sb_drop_capabilities();
|
|
435
|
-
nish_sb_landlock(caps, mode, read_dirs, write_dirs);
|
|
443
|
+
nish_sb_landlock(caps, mode, read_dirs, write_dirs, exec_paths);
|
|
436
444
|
/* Metadata writes stay EPERM inside a scoped write directory (open
|
|
437
445
|
* question 1): Landlock has no right for chmod, chown or xattrs. */
|
|
438
446
|
nish_sb_seccomp(write_dirs ? caps & ~CAP_BIT_FS_WRITE : caps);
|
|
@@ -457,11 +465,12 @@ static void nish_sb_none(void) {
|
|
|
457
465
|
_exit(126);
|
|
458
466
|
}
|
|
459
467
|
|
|
460
|
-
void nish_sandbox(int32_t caps, int32_t mode, const char *read_dirs, const char *write_dirs) {
|
|
468
|
+
void nish_sandbox(int32_t caps, int32_t mode, const char *read_dirs, const char *write_dirs, const char *exec_paths) {
|
|
461
469
|
(void)caps;
|
|
462
470
|
(void)mode;
|
|
463
471
|
(void)read_dirs;
|
|
464
472
|
(void)write_dirs;
|
|
473
|
+
(void)exec_paths;
|
|
465
474
|
nish_sb_none();
|
|
466
475
|
}
|
|
467
476
|
|