@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.
@@ -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 names a failure prints; a number for any other. */
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 ", 6);
191
- for (int i = 0; i < n; i++) buf[6 + i] = digits[n - 1 - i];
192
- buf[6 + n] = 0;
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
- char tail[40] = " (";
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`. A rule granting
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 | O_DIRECTORY);
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 directory of a scope list, each granting `rights`. */
301
- static void nish_sb_scope(int ruleset, const char *dirs, uint64_t rights, int32_t mode) {
302
- for (; dirs && *dirs; dirs += strlen(dirs) + 1) nish_sb_rule(ruleset, dirs, rights, mode);
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