@ai-ecoverse/slicc-kernel 1.153.0 → 1.154.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/README.md +2 -1
- package/dist/kernel-worker.js +49 -21
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +11 -4
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +49 -21
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +11 -4
- package/dist/process-worker.js.map +2 -2
- package/dist/types/kernel/net/tls-client.d.ts +4 -1
- package/dist/types/kernel/net/tls-engine.d.ts +5 -2
- package/dist/types/kernel/net/tls-server.d.ts +2 -0
- package/dist/types/kernel/net/tls-uplink.d.ts +2 -0
- package/dist/types/kernel/socket-syscalls.d.ts +3 -0
- package/dist/types/process/js/js-net.d.ts +3 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -454,7 +454,7 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
454
454
|
- `sudo(args)`, what the [`sudo`](#sudo) command calls: `args` are its arguments after `sudo`, and it resolves with `{ pid }`, the command the kernel started as the program's child, to `wait` for; `{ run }`, an argv to run as the caller (with `users: 'off'`); `{ list }`, the text of `sudo -l`; or `{ error, errno }`, the message to print;
|
|
455
455
|
- `crontab({ action, user?, text? })`, what the [`crontab`](#cron) command calls: `action` is `'list'`, `'remove'` or `'install'` (with the table as `text`), on the caller's own table or, for an effective uid of 0, on `user`'s. It resolves with `{ text }` (the table for `list`, `''` otherwise) or `{ error }`, the message to print. A malformed call is `EINVAL`;
|
|
456
456
|
- `net`: `listen({ port, host, backlog })` and `connect({ host, port, signal })` (`host` defaults to `127.0.0.1`) on the kernel's loopback, the same sockets WASIX programs use, so a JS server is reached by other processes, by the page as `<port>.kernel.localhost` (`loopbackFetch`, `dial`) and by attached clients. A listener has its `port` (as bound, so port `0` picks a free one), `accept({ signal })`, `close()` and `for await` over its connections. A connection has `local` and `remote` addresses, `readable` and `writable` web streams (closing the writable shuts down the sending side, so the peer reads the end), and `close()`. The loopback is IPv4 (`127.0.0.1` by default, or `0.0.0.0`), so an IPv6 host such as `::1` fails with `EADDRNOTAVAIL` for both `listen` and `connect`, and binding and connecting fail as they would for a WASIX program (`EADDRINUSE`, `ECONNREFUSED`, `EADDRNOTAVAIL`). `sync.net.listen` takes the same options and gives the same listener, bound and listening when it returns, so its `port` is known at once, as Node's `listen()` needs ([#1398](https://github.com/ai-ecoverse/slicc-kernel/issues/1398)); it needs the sync lane. A blocked accept, read or write waits without holding anything in the kernel, so aborting it or closing the socket leaves nothing behind. A write to a connection the peer closed fails with `EPIPE` and no `SIGPIPE`;
|
|
457
|
-
- `net.connect({ host, port, tls: { servername, alpn, rejectUnauthorized, ca, path, cert, key, passphrase, sni, checkHostname } })` takes one of two paths. By default it's **A1, TLS through the transport** ([#914](https://github.com/ai-ecoverse/slicc-kernel/issues/914)), described first below. For a destination the uplink routes it can also be **A2, a TLS handshake in the kernel** ([#922](https://github.com/ai-ecoverse/slicc-kernel/issues/922)), described after it. `conn.tls.path` (`'transport'` or `'kernel'`) and `conn.tls.substitutes` say which one was taken.
|
|
457
|
+
- `net.connect({ host, port, tls: { servername, alpn, rejectUnauthorized, ca, path, cert, key, passphrase, sni, checkHostname, keylog } })` takes one of two paths. By default it's **A1, TLS through the transport** ([#914](https://github.com/ai-ecoverse/slicc-kernel/issues/914)), described first below. For a destination the uplink routes it can also be **A2, a TLS handshake in the kernel** ([#922](https://github.com/ai-ecoverse/slicc-kernel/issues/922)), described after it. `conn.tls.path` (`'transport'` or `'kernel'`) and `conn.tls.substitutes` say which one was taken.
|
|
458
458
|
- **What it does:** there's no handshake in the kernel. The connection carries plain HTTP/1.1 (and WebSocket upgrades) to the realm proxy's HTTP path for the origin `https://<servername or host>[:port]`, exactly as a `CONNECT` through `127.0.0.1:3128` does after the kernel terminates its TLS. The transport (the page's `fetch` or WebSocket, or the uplink's) makes the real TLS connection and verifies it. So `https.request` over a socket, keep-alive agents, `agent-base` and `wss://` clients work with the transport's semantics. That includes sealed placeholders in headers, which reach the transport exactly as from `ctx.fetch`.
|
|
459
459
|
- **The `tls` property:** the connection's `tls` is `{ path: 'transport', substitutes: true, protocol: 'transport', alpnProtocol: 'http/1.1', authorized: true, servername }`. `authorized` means the transport verified the server. There's no peer certificate chain and no TLS version of the kernel's own, so don't read it as a real handshake's result.
|
|
460
460
|
- **Options:** the transport always verifies, so `rejectUnauthorized: false` is accepted and changes nothing. Outside the uplink, a custom `ca` is refused (`ENOTSUP`), and so is an `alpn` list without `http/1.1` (`EPROTONOSUPPORT`). The kernel's own loopback never goes to the transport. A `host` of `localhost` or `127.x` makes the handshake in the kernel instead (Loopback, below), so with nothing listening it's `ECONNREFUSED`. The rest of it (`*.localhost`, `0.0.0.0`, `::1`, and any loopback `servername` for another host) is `ENOTSUP` at connect, as `ctx.fetch` refuses `https:` and `wss:` there. `host.slicc.internal` goes to the transport under its policy, as for `ctx.fetch`.
|
|
@@ -502,6 +502,7 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
502
502
|
- `pfx` is `ENOTSUP`: pass `cert` and `key`.
|
|
503
503
|
- A server that requires a certificate and gets none refuses with an alert. On TLS 1.2 that fails the connect (`ERR_SSL_HANDSHAKE_FAILURE`). On TLS 1.3 the client's side of the handshake is already done, so the next read or write fails with `ERR_SSL_FATAL_ALERT`, as Node's fails after `secureConnect`.
|
|
504
504
|
- **A fatal alert from the server**, at the handshake or after it, is in the error: its `detail` ends in ` (alert <n>)`, and a JS program's error has `alert: n`, the alert's description number (116 certificate_required, 40 handshake_failure).
|
|
505
|
+
- **`keylog: true`** ([#1504](https://github.com/ai-ecoverse/slicc-kernel/issues/1504), tls-engine 4.2.0-10) gives the session's NSS key log lines as `conn.tls.keylog`, a frozen list of strings: `CLIENT_RANDOM …` on TLS 1.2, the four `…_TRAFFIC_SECRET…` lines on TLS 1.3, the lines Node's `keylog` event and `SSLKEYLOGFILE` carry. **They are the session's secrets: anyone holding them and a capture of the traffic can read it.** So a keylogged session is never sealed (`substitutes: false`; a placeholder goes out as written), and `keylog` on the transport path (A1, where the page makes the TLS) is `ENOTSUP`. Without `keylog` the engine collects nothing. The kernel wipes its copies once the lines are in the reply (the engine zeroes its buffer as it hands them out); the lines reach the program as JS strings, which can't be wiped, so they live as long as the program keeps them, as with its own `key`. A TLS server takes `keylog` too (`listen({ tls })`, `secure(conn, { server: true })`), for every connection it serves.
|
|
505
506
|
- **`sni: false`** sends no server name. The leaf is still checked against `servername` exactly as with SNI.
|
|
506
507
|
- **`checkHostname: false`** checks the chain (trust, validity, usage) but **not that the leaf names `servername`**. The connection reports `hostnameChecked: false`, and `hostnameMatches` says what the skipped check found.
|
|
507
508
|
- **The caller must then check the server's identity itself**, as slicc-jsh does with Node's `checkServerIdentity` and `peerCertificates[0]`; pass it only when the program supplied one.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -8049,7 +8049,7 @@ function satisfies(version, range2) {
|
|
|
8049
8049
|
}
|
|
8050
8050
|
|
|
8051
8051
|
// src/version.ts
|
|
8052
|
-
var KERNEL_VERSION = true ? "1.
|
|
8052
|
+
var KERNEL_VERSION = true ? "1.154.0" : "0.0.0-dev";
|
|
8053
8053
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8054
8054
|
function procVersion() {
|
|
8055
8055
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -17364,6 +17364,7 @@ var TLS_SERVER_REQUEST_CERT = 1;
|
|
|
17364
17364
|
var TLS_SERVER_REQUIRE_CERT = 2;
|
|
17365
17365
|
var TLS_SERVER_SELECT = 4;
|
|
17366
17366
|
var TLS_HANDSHAKE_SELECT = 2;
|
|
17367
|
+
var TLS_KEYLOG = 8;
|
|
17367
17368
|
var SCRATCH = 64 * 1024;
|
|
17368
17369
|
var EOF = -1;
|
|
17369
17370
|
var TlsError = class extends Error {
|
|
@@ -17431,6 +17432,9 @@ var TlsEngine = class {
|
|
|
17431
17432
|
if (!ptr) throw this.error(this.m._tls_last_error(), "server certificate");
|
|
17432
17433
|
return new TlsLeaf(this, this.m, ptr);
|
|
17433
17434
|
}
|
|
17435
|
+
get keylogs() {
|
|
17436
|
+
return typeof this.m._tls_keylog === "function";
|
|
17437
|
+
}
|
|
17434
17438
|
get serverSelect() {
|
|
17435
17439
|
return typeof this.m._tls_server_select === "function";
|
|
17436
17440
|
}
|
|
@@ -17461,9 +17465,11 @@ var TlsEngine = class {
|
|
|
17461
17465
|
this.m._free(ptr);
|
|
17462
17466
|
}
|
|
17463
17467
|
}
|
|
17464
|
-
intoScratch(fn) {
|
|
17468
|
+
intoScratch(fn, secret = false) {
|
|
17465
17469
|
const n = fn(this.scratch, SCRATCH);
|
|
17466
|
-
|
|
17470
|
+
const out = n > 0 ? this.taken(n) : n;
|
|
17471
|
+
if (secret && n > 0) this.m.HEAPU8.fill(0, this.scratch, this.scratch + n);
|
|
17472
|
+
return out;
|
|
17467
17473
|
}
|
|
17468
17474
|
get sessionControls() {
|
|
17469
17475
|
return typeof this.m._tls_client_cert === "function";
|
|
@@ -17674,6 +17680,14 @@ var TlsSession = class {
|
|
|
17674
17680
|
get version() {
|
|
17675
17681
|
return this.c._tls_version(this.ptr);
|
|
17676
17682
|
}
|
|
17683
|
+
keylog() {
|
|
17684
|
+
const drain2 = this.m._tls_keylog;
|
|
17685
|
+
const out = this.engine.intoScratch((p, max) => drain2(this.ptr, p, max), true);
|
|
17686
|
+
if (!(out instanceof Uint8Array)) return [];
|
|
17687
|
+
const text = new TextDecoder().decode(out);
|
|
17688
|
+
out.fill(0);
|
|
17689
|
+
return text.split("\n").filter((line) => line.length > 0);
|
|
17690
|
+
}
|
|
17677
17691
|
get sni() {
|
|
17678
17692
|
const out = this.engine.intoScratch(
|
|
17679
17693
|
(p, max) => this.m._tls_sni(this.ptr, p, max)
|
|
@@ -19310,7 +19324,7 @@ function isIP(name2) {
|
|
|
19310
19324
|
return ipv4Bytes(name2) !== void 0 || name2.includes(":") && ipv6Groups(name2) !== void 0;
|
|
19311
19325
|
}
|
|
19312
19326
|
function clientFlags(o) {
|
|
19313
|
-
return (o.insecure ? TLS_CLIENT_INSECURE_SKIP_VERIFY : 0) | (o.sni === false ? TLS_CLIENT_NO_SNI : 0) | (o.checkHostname === false ? TLS_CLIENT_VERIFY_CHAIN_ONLY : 0);
|
|
19327
|
+
return (o.insecure ? TLS_CLIENT_INSECURE_SKIP_VERIFY : 0) | (o.sni === false ? TLS_CLIENT_NO_SNI : 0) | (o.checkHostname === false ? TLS_CLIENT_VERIFY_CHAIN_ONLY : 0) | (o.keylog ? TLS_KEYLOG : 0);
|
|
19314
19328
|
}
|
|
19315
19329
|
function openSession(o) {
|
|
19316
19330
|
return o.engine.clientSession(o.store, o.servername, o.alpn, clientFlags(o));
|
|
@@ -19425,7 +19439,8 @@ async function infoOf(o, session) {
|
|
|
19425
19439
|
peerCertificates: chain2,
|
|
19426
19440
|
...controlsOf(o, session, protocol, chain2, shape),
|
|
19427
19441
|
verified: verdictOf(o, session),
|
|
19428
|
-
handshakeAt: Date.now()
|
|
19442
|
+
handshakeAt: Date.now(),
|
|
19443
|
+
...o.keylog ? { keylog: session.keylog() } : {}
|
|
19429
19444
|
};
|
|
19430
19445
|
}
|
|
19431
19446
|
var FATAL_ALERT = -30592;
|
|
@@ -19545,7 +19560,7 @@ async function serverHandshake(conn, session, inbox, hooks) {
|
|
|
19545
19560
|
}
|
|
19546
19561
|
}
|
|
19547
19562
|
}
|
|
19548
|
-
async function tlsServer(conn, session, clientCerts, select) {
|
|
19563
|
+
async function tlsServer(conn, session, clientCerts, select, keylog = false) {
|
|
19549
19564
|
let first;
|
|
19550
19565
|
const watched = {
|
|
19551
19566
|
...conn,
|
|
@@ -19568,7 +19583,8 @@ async function tlsServer(conn, session, clientCerts, select) {
|
|
|
19568
19583
|
servername: session.sni,
|
|
19569
19584
|
authorized: clientCerts && chain2.length > 0 && code === void 0,
|
|
19570
19585
|
...clientCerts && chain2.length > 0 && code ? { authorizationError: code } : {},
|
|
19571
|
-
peerCertificates: chain2
|
|
19586
|
+
peerCertificates: chain2,
|
|
19587
|
+
...keylog ? { keylog: session.keylog() } : {}
|
|
19572
19588
|
};
|
|
19573
19589
|
return { conn: plaintext(conn, session, inbox), info };
|
|
19574
19590
|
} catch (err) {
|
|
@@ -22461,7 +22477,8 @@ function sessionOf(info, servername) {
|
|
|
22461
22477
|
} : {},
|
|
22462
22478
|
servername,
|
|
22463
22479
|
peerCertificates: info.peerCertificates.map(encode),
|
|
22464
|
-
...controls(info)
|
|
22480
|
+
...controls(info),
|
|
22481
|
+
...info.keylog ? { keylog: info.keylog } : {}
|
|
22465
22482
|
};
|
|
22466
22483
|
}
|
|
22467
22484
|
function controls(info) {
|
|
@@ -22603,11 +22620,14 @@ async function prepareWith(support, req, servername, cert) {
|
|
|
22603
22620
|
insecure: req.insecure === true,
|
|
22604
22621
|
...req.sni === false ? { sni: false } : {},
|
|
22605
22622
|
...req.checkHostname === false ? { checkHostname: false } : {},
|
|
22623
|
+
...req.keylog === true ? { keylog: true } : {},
|
|
22606
22624
|
clientCertificate: cert !== void 0,
|
|
22607
22625
|
bundled: own === void 0
|
|
22608
22626
|
};
|
|
22609
22627
|
let session;
|
|
22610
22628
|
try {
|
|
22629
|
+
if (req.keylog === true && !engine.keylogs)
|
|
22630
|
+
throw new KernelError("ENOTSUP", "the installed tls engine has no key log");
|
|
22611
22631
|
const controls2 = req.sni === false || req.checkHostname === false || cert !== void 0;
|
|
22612
22632
|
if (controls2 && !engine.sessionControls) {
|
|
22613
22633
|
throw new KernelError("ENOTSUP", "the installed tls engine has no client certificates");
|
|
@@ -22655,7 +22675,7 @@ function secure(prepared, seal) {
|
|
|
22655
22675
|
try {
|
|
22656
22676
|
const made2 = await tlsClient(raw2, prepared.options);
|
|
22657
22677
|
const engine = prepared.options.engine;
|
|
22658
|
-
const session = sealing({ ...seal, engine }, raw2, made2.info, prepared.options.servername);
|
|
22678
|
+
const session = prepared.options.keylog ? void 0 : sealing({ ...seal, engine }, raw2, made2.info, prepared.options.servername);
|
|
22659
22679
|
if (!session) return made2;
|
|
22660
22680
|
made2.info.substitutes = substitutes(session);
|
|
22661
22681
|
if (made2.info.substitutes && session.verified)
|
|
@@ -22768,25 +22788,31 @@ function certFlags(req) {
|
|
|
22768
22788
|
if (!req.requestCert) return 0;
|
|
22769
22789
|
return req.rejectUnauthorized === false ? TLS_SERVER_REQUEST_CERT : TLS_SERVER_REQUIRE_CERT;
|
|
22770
22790
|
}
|
|
22771
|
-
|
|
22772
|
-
|
|
22791
|
+
var CERT_FLAGS = TLS_SERVER_REQUEST_CERT | TLS_SERVER_REQUIRE_CERT;
|
|
22792
|
+
function selectFlag(req) {
|
|
22793
|
+
if (!req.select) return 0;
|
|
22773
22794
|
if (req.alpn && req.alpn.length > 0)
|
|
22774
22795
|
throw new KernelError("EINVAL", "ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS: alpn with select");
|
|
22775
|
-
return
|
|
22796
|
+
return TLS_SERVER_SELECT;
|
|
22797
|
+
}
|
|
22798
|
+
function flagsOf(req) {
|
|
22799
|
+
return certFlags(req) | selectFlag(req) | (req.keylog ? TLS_KEYLOG : 0);
|
|
22776
22800
|
}
|
|
22777
|
-
async function engineOf(proc, select) {
|
|
22801
|
+
async function engineOf(proc, { select, keylog }) {
|
|
22778
22802
|
const engine = await supportOf(proc.net).engine();
|
|
22779
22803
|
if (!engine.serverSessions)
|
|
22780
22804
|
throw new KernelError("ENOTSUP", "the installed tls engine has no server sessions");
|
|
22805
|
+
if (keylog && !engine.keylogs)
|
|
22806
|
+
throw new KernelError("ENOTSUP", "the installed tls engine has no key log");
|
|
22781
22807
|
if (select && !engine.serverSelect)
|
|
22782
22808
|
throw new KernelError("ENOTSUP", "the installed tls engine has no server selection");
|
|
22783
22809
|
return engine;
|
|
22784
22810
|
}
|
|
22785
|
-
async function importedLeaf(proc, own,
|
|
22811
|
+
async function importedLeaf(proc, own, needs = {}) {
|
|
22786
22812
|
const cert = certBytes(own);
|
|
22787
22813
|
let engine;
|
|
22788
22814
|
try {
|
|
22789
|
-
engine = await engineOf(proc,
|
|
22815
|
+
engine = await engineOf(proc, needs);
|
|
22790
22816
|
} catch (err) {
|
|
22791
22817
|
wipe(cert);
|
|
22792
22818
|
throw err;
|
|
@@ -22816,12 +22842,12 @@ async function serverOf(proc, req) {
|
|
|
22816
22842
|
delete req.cert;
|
|
22817
22843
|
if (!own && !req.select)
|
|
22818
22844
|
throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: a server needs cert and key");
|
|
22819
|
-
const { engine, leaf } = own ? await importedLeaf(proc, own, req
|
|
22845
|
+
const { engine, leaf } = own ? await importedLeaf(proc, own, req) : { engine: await engineOf(proc, { ...req, select: true }), leaf: void 0 };
|
|
22820
22846
|
let mine;
|
|
22821
22847
|
try {
|
|
22822
22848
|
mine = req.ca && req.ca.length > 0 ? storeOf(engine, req.ca) : void 0;
|
|
22823
22849
|
const flags2 = flagsOf(req);
|
|
22824
|
-
const store = mine ?? (flags2 &
|
|
22850
|
+
const store = mine ?? (flags2 & CERT_FLAGS ? await defaultStore(engine, supportOf(proc.net).roots) : void 0);
|
|
22825
22851
|
return {
|
|
22826
22852
|
engine,
|
|
22827
22853
|
leaf,
|
|
@@ -22855,7 +22881,8 @@ function servedOf(info) {
|
|
|
22855
22881
|
authorized: info.authorized,
|
|
22856
22882
|
...info.authorizationError ? { authorizationError: info.authorizationError } : {},
|
|
22857
22883
|
peerCertificates: info.peerCertificates.map(encode),
|
|
22858
|
-
cipher: info.cipher
|
|
22884
|
+
cipher: info.cipher,
|
|
22885
|
+
...info.keylog ? { keylog: info.keylog } : {}
|
|
22859
22886
|
};
|
|
22860
22887
|
}
|
|
22861
22888
|
var selections = /* @__PURE__ */ new WeakMap();
|
|
@@ -22878,13 +22905,14 @@ async function serveSecure(proc, socket, req) {
|
|
|
22878
22905
|
const { engine, leaf, alpn, store, flags: flags2 } = server;
|
|
22879
22906
|
const limit = handshakeSignal(proc, server.timeoutMs);
|
|
22880
22907
|
const { pausedAt, select } = pausing(limit.signal);
|
|
22881
|
-
const certs = (flags2 &
|
|
22908
|
+
const certs = (flags2 & CERT_FLAGS) !== 0;
|
|
22882
22909
|
const run2 = socket.secureLink(
|
|
22883
22910
|
(raw2) => tlsServer(
|
|
22884
22911
|
raw2,
|
|
22885
22912
|
engine.serverSession(leaf, alpn, store, flags2),
|
|
22886
22913
|
certs,
|
|
22887
|
-
flags2 & TLS_SERVER_SELECT ? select : void 0
|
|
22914
|
+
flags2 & TLS_SERVER_SELECT ? select : void 0,
|
|
22915
|
+
(flags2 & TLS_KEYLOG) !== 0
|
|
22888
22916
|
),
|
|
22889
22917
|
limit.signal,
|
|
22890
22918
|
(reason) => !shared && reason === timedOut
|
|
@@ -23354,7 +23382,7 @@ var NOT_HTTP_PORTS = /* @__PURE__ */ new Set([
|
|
|
23354
23382
|
]);
|
|
23355
23383
|
var HOST_NAME = /^[A-Za-z0-9.-]+$|^\[?[0-9A-Fa-f:.]+\]?$/;
|
|
23356
23384
|
function sessionControls(req) {
|
|
23357
|
-
return req.sni === false || req.checkHostname === false || req.clientCert !== void 0;
|
|
23385
|
+
return req.sni === false || req.checkHostname === false || req.clientCert !== void 0 || req.keylog === true;
|
|
23358
23386
|
}
|
|
23359
23387
|
function wantsKernel(req) {
|
|
23360
23388
|
if (req.path === "kernel") return true;
|