@ai-ecoverse/slicc-kernel 1.148.1 → 1.149.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 +10 -1
- package/dist/kernel-worker.js +295 -12
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +209 -75
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +295 -12
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +209 -75
- package/dist/process-worker.js.map +3 -3
- package/dist/types/kernel/net/tls-client.d.ts +12 -0
- package/dist/types/kernel/net/tls-engine.d.ts +7 -1
- package/dist/types/kernel/net/tls-server.d.ts +34 -0
- package/dist/types/kernel/net/tls-uplink.d.ts +11 -1
- package/dist/types/kernel/socket-syscalls.d.ts +11 -0
- package/dist/types/kernel/socket.d.ts +9 -0
- package/dist/types/process/js/js-net.d.ts +16 -0
- package/package.json +3 -3
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
|
- `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.
|
|
455
455
|
- **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`.
|
|
456
456
|
- **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.
|
|
457
|
-
- **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
|
|
457
|
+
- **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`.
|
|
458
458
|
- **Non-HTTP protocols aren't carried:**
|
|
459
459
|
- the well-known TLS ports of other protocols (465 SMTPS, 993 IMAPS, 995 POP3S, 636 LDAPS, 853, 8883 MQTT, 6697 IRC, 5671 AMQP and the like) are refused at `connect` with `EPROTONOSUPPORT`;
|
|
460
460
|
- non-HTTP first bytes on any other port get a `400` and a close;
|
|
@@ -504,6 +504,15 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
504
504
|
- **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.
|
|
505
505
|
- With `rejectUnauthorized: false` it's `EINVAL`.
|
|
506
506
|
- **STARTTLS:** `net.connect({ socket: conn, host, tls })` upgrades an open plain connection to an uplink destination in place, as Node's `tls.connect({ socket })` does. It resolves with a connection whose streams now carry the decrypted bytes and whose `tls` is the session. Don't hold a pending read on the plain connection while it upgrades. Pass `host` (or `tls.servername`): the plain connection only knows the address it reached, so without a name the certificate is checked against that IP, and a certificate for the host name fails.
|
|
507
|
+
- **Loopback:** `net.connect({ host, port, tls })` to `localhost` or a `127.x` address makes the handshake in the kernel too, over the loopback connection, with the same options as A2 (`ca`, `cert`/`key`, `sni`, `checkHostname`). Nothing on loopback is ever substituted (`substitutes: false`).
|
|
508
|
+
- **A TLS server** ([#1401](https://github.com/ai-ecoverse/slicc-kernel/issues/1401)), with the program's own certificate and key: `net.listen({ port, host, tls: { cert, key, passphrase, alpn, ca, requestCert, rejectUnauthorized } })` (and `sync.net.listen`) gives a listener whose connections are already secured, and `net.secure(conn, { server: true, cert, key, … })` secures one accepted connection. `net.secure(conn, tls)` without `server: true` is the client upgrade, as `connect({ socket })`.
|
|
509
|
+
- `cert` and `key` take the same forms as a client certificate, and are refused the same way, at `listen` (or `secure`) before anything is accepted: a key for another certificate is `ERR_OSSL_X509_KEY_VALUES_MISMATCH`, a wrong passphrase `ERR_OSSL_BAD_DECRYPT`.
|
|
510
|
+
- `alpn` is the server's list in order of preference; the first one the client also offers wins, and a client that offers ALPN but none of these fails the handshake.
|
|
511
|
+
- `requestCert` needs `ca` (the roots a client certificate must reach; else `EINVAL`). With it the handshake fails without a client certificate that verifies, unless `rejectUnauthorized: false`, which only asks for one and reports the verdict.
|
|
512
|
+
- A connection's `tls` is `{ path: 'kernel', protocol, alpnProtocol, servername (the SNI the client sent, or ''), authorized (whether a client certificate verified), authorizationError?, peerCertificates (the client's chain) }`.
|
|
513
|
+
- On a `listen({ tls })` listener, the kernel handshakes accepted connections concurrently, from the first `accept` on, and `accept` (and `for await`) yields each as its handshake finishes, so a client that connects and stays silent doesn't hold up the next. It holds at most `backlog` (up to 64) connections in a handshake or handshaken but not yet taken; beyond that it stops accepting, leaving the rest in the kernel's backlog, until one finishes or is taken. A connection whose handshake fails is closed and never yielded. `secure()` rejects instead.
|
|
514
|
+
- `handshakeTimeout` (milliseconds, 120000 by default, as Node's) ends a handshake that hasn't finished: the connection is closed with `ERR_TLS_HANDSHAKE_TIMEOUT`. A handshake, server or loopback client, waits without holding the program's other calls, and an aborted `accept({ signal })` or `connect({ signal })` ends it. A signal delivered to the program doesn't interrupt a handshake, as with `SA_RESTART`.
|
|
515
|
+
- **The key:** the kernel hands it to the engine once per listener (once per `secure()` call) and wipes its own copies; the engine keeps its copy until the listener is closed, and not in errors, logs, `ps`, `/proc` or the page. A forked child that inherits the listener shares that one copy, and it's freed when the last process closes it. **The program's own `key` string or `Buffer` stays in its realm**: JavaScript strings can't be wiped, so the kernel can't erase what the program holds.
|
|
507
516
|
- **Sealed values in an A2 session** ([#1090](https://github.com/ai-ecoverse/slicc-kernel/issues/1090)) are substituted only at a credential position, only in a session the kernel verified itself (by its bundled roots, or by a vault anchor), and only as the vault allows. `substitutes` means the session **may** substitute: a placeholder at a recognized position will be offered to the vault, and whether a value comes back is the vault's and the anchor check's decision. It's true when all of these hold:
|
|
508
517
|
- `verified` is true, or the engine can verify a chain on its own (wasm-tls-engine 4.2.0-5's `tls_chain_verify`) and the server name isn't an IP literal, so a vault anchor may verify the session (below);
|
|
509
518
|
- ALPN didn't agree `h2`;
|
package/dist/kernel-worker.js
CHANGED
|
@@ -5954,6 +5954,9 @@ function link(a, b) {
|
|
|
5954
5954
|
a.attach({ rx: ba, tx: ab });
|
|
5955
5955
|
b.attach({ rx: ab, tx: ba });
|
|
5956
5956
|
}
|
|
5957
|
+
var SERVE_CHUNK = 64 * 1024;
|
|
5958
|
+
var NEVER_ABORTED = new AbortController().signal;
|
|
5959
|
+
var EMPTY = new Uint8Array(0);
|
|
5957
5960
|
var KernelSocket = class _KernelSocket {
|
|
5958
5961
|
net;
|
|
5959
5962
|
domain;
|
|
@@ -6151,6 +6154,54 @@ var KernelSocket = class _KernelSocket {
|
|
|
6151
6154
|
if (this.state !== "open") throw new KernelError("EISCONN");
|
|
6152
6155
|
return this.dial(addr, route2, secure2);
|
|
6153
6156
|
}
|
|
6157
|
+
serving;
|
|
6158
|
+
served = false;
|
|
6159
|
+
get loopbackLinked() {
|
|
6160
|
+
return this.state === "connected" && !this.switch;
|
|
6161
|
+
}
|
|
6162
|
+
async secureLink(make, signal = NEVER_ABORTED) {
|
|
6163
|
+
const raw2 = this.connection();
|
|
6164
|
+
if (this.switch || this.served || this.writeShut || this.readShut)
|
|
6165
|
+
throw new KernelError("EINVAL");
|
|
6166
|
+
signal.throwIfAborted();
|
|
6167
|
+
this.served = true;
|
|
6168
|
+
let handshaking = true;
|
|
6169
|
+
const view2 = {
|
|
6170
|
+
localAddr: "",
|
|
6171
|
+
remoteAddr: "",
|
|
6172
|
+
read: async () => {
|
|
6173
|
+
const bytes = await raw2.rx.read(SERVE_CHUNK, handshaking ? signal : void 0).catch(() => EMPTY);
|
|
6174
|
+
return bytes.length > 0 ? bytes : null;
|
|
6175
|
+
},
|
|
6176
|
+
write: (bytes) => raw2.tx.write(bytes),
|
|
6177
|
+
closeWrite: () => raw2.tx.closeWrite(),
|
|
6178
|
+
close: () => {
|
|
6179
|
+
raw2.rx.closeRead();
|
|
6180
|
+
raw2.tx.closeWrite();
|
|
6181
|
+
}
|
|
6182
|
+
};
|
|
6183
|
+
let made2;
|
|
6184
|
+
const abandon = () => view2.close();
|
|
6185
|
+
signal.addEventListener("abort", abandon, { once: true });
|
|
6186
|
+
try {
|
|
6187
|
+
made2 = await make(view2);
|
|
6188
|
+
} catch (err) {
|
|
6189
|
+
view2.close();
|
|
6190
|
+
this.readShut = true;
|
|
6191
|
+
this.writeShut = true;
|
|
6192
|
+
this.changes.wake();
|
|
6193
|
+
throw signal.aborted ? signal.reason : err;
|
|
6194
|
+
} finally {
|
|
6195
|
+
handshaking = false;
|
|
6196
|
+
signal.removeEventListener("abort", abandon);
|
|
6197
|
+
}
|
|
6198
|
+
this.link = uplinkLink(made2.conn, (err) => {
|
|
6199
|
+
this.reset = err instanceof KernelError && err.code === "EPROTO" ? err : new KernelError("ECONNRESET");
|
|
6200
|
+
this.changes.wake();
|
|
6201
|
+
});
|
|
6202
|
+
this.changes.wake();
|
|
6203
|
+
return made2.info;
|
|
6204
|
+
}
|
|
6154
6205
|
upgradeSecure(make) {
|
|
6155
6206
|
if (this.state !== "connected") throw new KernelError("ENOTCONN");
|
|
6156
6207
|
if (!this.switch) throw new KernelError("EOPNOTSUPP");
|
|
@@ -6282,6 +6333,8 @@ var KernelSocket = class _KernelSocket {
|
|
|
6282
6333
|
}
|
|
6283
6334
|
if (this.boundAt) this.net.release(this.boundAt, this, this.named);
|
|
6284
6335
|
this.boundAt = void 0;
|
|
6336
|
+
this.serving?.release();
|
|
6337
|
+
this.serving = void 0;
|
|
6285
6338
|
this.changes.wake();
|
|
6286
6339
|
}
|
|
6287
6340
|
static pair(net, domain, cred) {
|
|
@@ -7855,7 +7908,7 @@ function satisfies(version, range2) {
|
|
|
7855
7908
|
}
|
|
7856
7909
|
|
|
7857
7910
|
// src/version.ts
|
|
7858
|
-
var KERNEL_VERSION = true ? "1.
|
|
7911
|
+
var KERNEL_VERSION = true ? "1.149.0" : "0.0.0-dev";
|
|
7859
7912
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7860
7913
|
function procVersion() {
|
|
7861
7914
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10354,7 +10407,7 @@ async function followLinks(fs, path) {
|
|
|
10354
10407
|
return current;
|
|
10355
10408
|
}
|
|
10356
10409
|
var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
|
|
10357
|
-
var
|
|
10410
|
+
var EMPTY2 = new Uint8Array(0);
|
|
10358
10411
|
function quiet(promise) {
|
|
10359
10412
|
return promise.catch(() => void 0);
|
|
10360
10413
|
}
|
|
@@ -10437,13 +10490,13 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
|
|
|
10437
10490
|
return target?.path;
|
|
10438
10491
|
}
|
|
10439
10492
|
const absent2 = (err) => {
|
|
10440
|
-
if (missing3(err)) return
|
|
10493
|
+
if (missing3(err)) return EMPTY2;
|
|
10441
10494
|
throw err;
|
|
10442
10495
|
};
|
|
10443
10496
|
async function content(path, err) {
|
|
10444
10497
|
if (!missing3(err) || !(await linked2(path))?.isFile) throw err;
|
|
10445
10498
|
const target = await redirected2(path);
|
|
10446
|
-
return target === void 0 ?
|
|
10499
|
+
return target === void 0 ? EMPTY2 : fs.readFileBuffer(target).catch(absent2);
|
|
10447
10500
|
}
|
|
10448
10501
|
const ranged2 = rangedOps(fs);
|
|
10449
10502
|
async function stat(path, real) {
|
|
@@ -16717,6 +16770,8 @@ function realmCa(owner, store) {
|
|
|
16717
16770
|
var TLS_CLIENT_INSECURE_SKIP_VERIFY = 1;
|
|
16718
16771
|
var TLS_CLIENT_NO_SNI = 2;
|
|
16719
16772
|
var TLS_CLIENT_VERIFY_CHAIN_ONLY = 4;
|
|
16773
|
+
var TLS_SERVER_REQUEST_CERT = 1;
|
|
16774
|
+
var TLS_SERVER_REQUIRE_CERT = 2;
|
|
16720
16775
|
var SCRATCH = 64 * 1024;
|
|
16721
16776
|
var EOF = -1;
|
|
16722
16777
|
var TlsError = class extends Error {
|
|
@@ -16765,6 +16820,35 @@ var TlsEngine = class {
|
|
|
16765
16820
|
this.m._free(name2);
|
|
16766
16821
|
}
|
|
16767
16822
|
}
|
|
16823
|
+
get serverSessions() {
|
|
16824
|
+
return typeof this.m._tls_server_new === "function";
|
|
16825
|
+
}
|
|
16826
|
+
importLeaf(chain2, key, pass2) {
|
|
16827
|
+
const leafImport = this.m._tls_leaf_import;
|
|
16828
|
+
const keyLen = key[0] === 45 ? key.length + 1 : key.length;
|
|
16829
|
+
const ptr = this.withBytes(
|
|
16830
|
+
chain2,
|
|
16831
|
+
(c) => this.withSecret(
|
|
16832
|
+
key,
|
|
16833
|
+
(k) => this.withSecret(
|
|
16834
|
+
pass2,
|
|
16835
|
+
(p) => leafImport(c, chain2.length, k, keyLen, pass2.length > 0 ? p : 0, pass2.length)
|
|
16836
|
+
)
|
|
16837
|
+
)
|
|
16838
|
+
);
|
|
16839
|
+
if (!ptr) throw this.error(this.m._tls_last_error(), "server certificate");
|
|
16840
|
+
return new TlsLeaf(this, this.m, ptr);
|
|
16841
|
+
}
|
|
16842
|
+
serverSession(leaf, alpn, clientCa, flags2) {
|
|
16843
|
+
const open2 = this.m._tls_server_new;
|
|
16844
|
+
const wire = alpnWire(alpn);
|
|
16845
|
+
const ptr = this.withBytes(
|
|
16846
|
+
wire,
|
|
16847
|
+
(p) => open2(leaf.ptr, wire.length > 0 ? p : 0, wire.length, clientCa?.ptr ?? 0, flags2)
|
|
16848
|
+
);
|
|
16849
|
+
if (!ptr) throw this.error(this.m._tls_last_error(), "tls server");
|
|
16850
|
+
return new TlsSession(this, this.m, ptr);
|
|
16851
|
+
}
|
|
16768
16852
|
withBytes(bytes, fn) {
|
|
16769
16853
|
const ptr = this.put(bytes);
|
|
16770
16854
|
try {
|
|
@@ -16969,6 +17053,12 @@ var TlsSession = class {
|
|
|
16969
17053
|
get version() {
|
|
16970
17054
|
return this.c._tls_version(this.ptr);
|
|
16971
17055
|
}
|
|
17056
|
+
get sni() {
|
|
17057
|
+
const out = this.engine.intoScratch(
|
|
17058
|
+
(p, max) => this.m._tls_sni(this.ptr, p, max)
|
|
17059
|
+
);
|
|
17060
|
+
return out instanceof Uint8Array ? new TextDecoder().decode(out) : "";
|
|
17061
|
+
}
|
|
16972
17062
|
get alpn() {
|
|
16973
17063
|
const out = this.engine.intoScratch((p, max) => this.c._tls_alpn(this.ptr, p, max));
|
|
16974
17064
|
return out instanceof Uint8Array ? new TextDecoder().decode(out) : void 0;
|
|
@@ -17471,7 +17561,7 @@ var Upload = class {
|
|
|
17471
17561
|
}
|
|
17472
17562
|
}
|
|
17473
17563
|
};
|
|
17474
|
-
var
|
|
17564
|
+
var EMPTY3 = new Uint8Array(0);
|
|
17475
17565
|
function message2(e) {
|
|
17476
17566
|
return e instanceof Error ? e.message : String(e);
|
|
17477
17567
|
}
|
|
@@ -17546,8 +17636,8 @@ var HttpHandles = class {
|
|
|
17546
17636
|
const entry = {
|
|
17547
17637
|
abort,
|
|
17548
17638
|
head: Promise.resolve({ ok: true, kind: "void" }),
|
|
17549
|
-
body: once(
|
|
17550
|
-
left:
|
|
17639
|
+
body: once(EMPTY3),
|
|
17640
|
+
left: EMPTY3,
|
|
17551
17641
|
done: false,
|
|
17552
17642
|
reading: Promise.resolve(),
|
|
17553
17643
|
cancel: async () => void 0,
|
|
@@ -18785,6 +18875,46 @@ async function tlsClient(conn, o, taken = new Uint8Array(0)) {
|
|
|
18785
18875
|
throw err;
|
|
18786
18876
|
}
|
|
18787
18877
|
}
|
|
18878
|
+
async function serverHandshake(conn, session, inbox) {
|
|
18879
|
+
for (; ; ) {
|
|
18880
|
+
let done5;
|
|
18881
|
+
try {
|
|
18882
|
+
done5 = session.handshake();
|
|
18883
|
+
} catch (err) {
|
|
18884
|
+
await send(conn, session).catch(() => void 0);
|
|
18885
|
+
const text = err instanceof TlsError ? err.message : String(err);
|
|
18886
|
+
throw new KernelError("EPROTO", `ERR_SSL_HANDSHAKE_FAILURE: ${text}${alertOf(session)}`);
|
|
18887
|
+
}
|
|
18888
|
+
await send(conn, session);
|
|
18889
|
+
if (done5) return;
|
|
18890
|
+
if (!await inbox.more()) {
|
|
18891
|
+
throw new KernelError(
|
|
18892
|
+
"ECONNRESET",
|
|
18893
|
+
"the client closed the connection during the TLS handshake"
|
|
18894
|
+
);
|
|
18895
|
+
}
|
|
18896
|
+
}
|
|
18897
|
+
}
|
|
18898
|
+
async function tlsServer(conn, session, clientCerts) {
|
|
18899
|
+
const inbox = new Ciphertext(conn, session);
|
|
18900
|
+
try {
|
|
18901
|
+
await serverHandshake(conn, session, inbox);
|
|
18902
|
+
const chain2 = session.peerChain();
|
|
18903
|
+
const code = clientCerts ? verifyCode(session.verifyResult) : void 0;
|
|
18904
|
+
const info = {
|
|
18905
|
+
protocol: session.version === 772 ? "TLSv1.3" : "TLSv1.2",
|
|
18906
|
+
alpnProtocol: session.alpn ?? false,
|
|
18907
|
+
servername: session.sni,
|
|
18908
|
+
authorized: clientCerts && chain2.length > 0 && code === void 0,
|
|
18909
|
+
...clientCerts && chain2.length > 0 && code ? { authorizationError: code } : {},
|
|
18910
|
+
peerCertificates: chain2
|
|
18911
|
+
};
|
|
18912
|
+
return { conn: plaintext(conn, session, inbox), info };
|
|
18913
|
+
} catch (err) {
|
|
18914
|
+
session.free();
|
|
18915
|
+
throw err;
|
|
18916
|
+
}
|
|
18917
|
+
}
|
|
18788
18918
|
|
|
18789
18919
|
// src/kernel/net/tls-chain.ts
|
|
18790
18920
|
var CN_MISMATCH = 4;
|
|
@@ -19237,7 +19367,7 @@ var RANK = {
|
|
|
19237
19367
|
};
|
|
19238
19368
|
var CR2 = 13;
|
|
19239
19369
|
var LF2 = 10;
|
|
19240
|
-
var
|
|
19370
|
+
var EMPTY4 = new Uint8Array(0);
|
|
19241
19371
|
function register(automaton, sources, ids, bytes, source) {
|
|
19242
19372
|
const key = Array.from(bytes).join(",");
|
|
19243
19373
|
let id2 = ids.get(key);
|
|
@@ -19417,12 +19547,12 @@ function compile(secrets, options = {}) {
|
|
|
19417
19547
|
return { text: decoder4.decode(bytes), hits };
|
|
19418
19548
|
},
|
|
19419
19549
|
stream() {
|
|
19420
|
-
let pending =
|
|
19550
|
+
let pending = EMPTY4;
|
|
19421
19551
|
const hits = /* @__PURE__ */ new Map();
|
|
19422
19552
|
const run2 = (final) => {
|
|
19423
19553
|
if (c.secrets.length === 0 || pending.length === 0) {
|
|
19424
19554
|
const out = pending;
|
|
19425
|
-
pending =
|
|
19555
|
+
pending = EMPTY4;
|
|
19426
19556
|
return out;
|
|
19427
19557
|
}
|
|
19428
19558
|
const result = pass(c, pending, final);
|
|
@@ -19436,7 +19566,7 @@ function compile(secrets, options = {}) {
|
|
|
19436
19566
|
return run2(false);
|
|
19437
19567
|
},
|
|
19438
19568
|
idle() {
|
|
19439
|
-
return pending.length < FRAGMENT ? run2(true) :
|
|
19569
|
+
return pending.length < FRAGMENT ? run2(true) : EMPTY4;
|
|
19440
19570
|
},
|
|
19441
19571
|
end() {
|
|
19442
19572
|
return run2(true);
|
|
@@ -21598,6 +21728,7 @@ function sealedConn(conn, s) {
|
|
|
21598
21728
|
}
|
|
21599
21729
|
|
|
21600
21730
|
// src/kernel/net/tls-uplink.ts
|
|
21731
|
+
var handshakeAbort = (proc) => proc.cancelled ?? proc.blocking();
|
|
21601
21732
|
var isIP2 = (text) => ipv4Bytes(text) !== void 0 || text.includes(":") && ipv6Groups(text) !== void 0;
|
|
21602
21733
|
var LABEL2 = /^(?!-)[A-Za-z0-9-]{1,63}(?<!-)$/;
|
|
21603
21734
|
function serverName(name2) {
|
|
@@ -21718,6 +21849,9 @@ function takeCert(req) {
|
|
|
21718
21849
|
const cert = req.clientCert;
|
|
21719
21850
|
if (!cert) return void 0;
|
|
21720
21851
|
delete req.clientCert;
|
|
21852
|
+
return certBytes(cert);
|
|
21853
|
+
}
|
|
21854
|
+
function certBytes(cert) {
|
|
21721
21855
|
if (!Array.isArray(cert.chain) || cert.chain.length === 0 || typeof cert.key !== "string")
|
|
21722
21856
|
throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: cert and key go together");
|
|
21723
21857
|
let ders;
|
|
@@ -21893,8 +22027,32 @@ async function connectSecure(proc, socket, host, port, req) {
|
|
|
21893
22027
|
}
|
|
21894
22028
|
return sessionOf(info, servername);
|
|
21895
22029
|
}
|
|
22030
|
+
async function loopbackSecure(proc, socket, servername, req) {
|
|
22031
|
+
const prepared = await prepare2(supportOf(proc.net), req, servername);
|
|
22032
|
+
let ran = false;
|
|
22033
|
+
try {
|
|
22034
|
+
const info = await socket.secureLink(async (raw2) => {
|
|
22035
|
+
ran = true;
|
|
22036
|
+
try {
|
|
22037
|
+
return await tlsClient(raw2, prepared.options);
|
|
22038
|
+
} finally {
|
|
22039
|
+
prepared.own?.release();
|
|
22040
|
+
}
|
|
22041
|
+
}, handshakeAbort(proc));
|
|
22042
|
+
return sessionOf({ ...info, substitutes: false }, servername);
|
|
22043
|
+
} finally {
|
|
22044
|
+
if (!ran) {
|
|
22045
|
+
prepared.options.session.free();
|
|
22046
|
+
prepared.own?.release();
|
|
22047
|
+
}
|
|
22048
|
+
}
|
|
22049
|
+
}
|
|
21896
22050
|
async function upgradeSecure(proc, socket, req) {
|
|
21897
22051
|
const peer = socket.peer;
|
|
22052
|
+
if (socket.loopbackLinked) {
|
|
22053
|
+
const host2 = req.servername || (peer?.family === "inet" ? peer.host : "localhost");
|
|
22054
|
+
return loopbackSecure(proc, socket, serverName(host2), req);
|
|
22055
|
+
}
|
|
21898
22056
|
const host = req.servername || (peer?.family === "inet" ? peer.host : "");
|
|
21899
22057
|
const servername = serverName(host);
|
|
21900
22058
|
const port = peer?.family === "inet" ? peer.port : 0;
|
|
@@ -21909,6 +22067,120 @@ async function upgradeSecure(proc, socket, req) {
|
|
|
21909
22067
|
}
|
|
21910
22068
|
}
|
|
21911
22069
|
|
|
22070
|
+
// src/kernel/net/tls-server.ts
|
|
22071
|
+
var HANDSHAKE_TIMEOUT_MS = 12e4;
|
|
22072
|
+
function leafOf(engine, cert) {
|
|
22073
|
+
try {
|
|
22074
|
+
return engine.importLeaf(cert.chain, cert.key, cert.pass);
|
|
22075
|
+
} catch (err) {
|
|
22076
|
+
throw keyRefused(err, cert.pass.length > 0 && encryptedKey(cert.key));
|
|
22077
|
+
} finally {
|
|
22078
|
+
wipe(cert);
|
|
22079
|
+
}
|
|
22080
|
+
}
|
|
22081
|
+
function storeOf(engine, ca) {
|
|
22082
|
+
const store = engine.store();
|
|
22083
|
+
try {
|
|
22084
|
+
for (const der2 of ca) store.add(base64Der(der2));
|
|
22085
|
+
return store;
|
|
22086
|
+
} catch (err) {
|
|
22087
|
+
store.release();
|
|
22088
|
+
throw new KernelError("EINVAL", `ERR_INVALID_ARG_VALUE: ca: ${err.message}`);
|
|
22089
|
+
}
|
|
22090
|
+
}
|
|
22091
|
+
function flagsOf(req, store) {
|
|
22092
|
+
if (!req.requestCert) return 0;
|
|
22093
|
+
if (!store) throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: requestCert needs a ca");
|
|
22094
|
+
return req.rejectUnauthorized === false ? TLS_SERVER_REQUEST_CERT : TLS_SERVER_REQUIRE_CERT;
|
|
22095
|
+
}
|
|
22096
|
+
function timeoutOf(req) {
|
|
22097
|
+
const ms = req.handshakeTimeout ?? HANDSHAKE_TIMEOUT_MS;
|
|
22098
|
+
if (!Number.isInteger(ms) || ms <= 0)
|
|
22099
|
+
throw new KernelError(
|
|
22100
|
+
"EINVAL",
|
|
22101
|
+
"ERR_OUT_OF_RANGE: handshakeTimeout must be a positive integer"
|
|
22102
|
+
);
|
|
22103
|
+
return ms;
|
|
22104
|
+
}
|
|
22105
|
+
function handshakeSignal(proc, ms) {
|
|
22106
|
+
const timer = new AbortController();
|
|
22107
|
+
const id2 = setTimeout(
|
|
22108
|
+
() => timer.abort(new KernelError("EPROTO", "ERR_TLS_HANDSHAKE_TIMEOUT: TLS handshake timeout")),
|
|
22109
|
+
ms
|
|
22110
|
+
);
|
|
22111
|
+
return {
|
|
22112
|
+
signal: AbortSignal.any([handshakeAbort(proc), timer.signal]),
|
|
22113
|
+
done: () => clearTimeout(id2)
|
|
22114
|
+
};
|
|
22115
|
+
}
|
|
22116
|
+
async function serverOf(proc, req) {
|
|
22117
|
+
const own = req.cert;
|
|
22118
|
+
delete req.cert;
|
|
22119
|
+
if (!own) throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: a server needs cert and key");
|
|
22120
|
+
const cert = certBytes(own);
|
|
22121
|
+
let engine;
|
|
22122
|
+
try {
|
|
22123
|
+
engine = await supportOf(proc.net).engine();
|
|
22124
|
+
if (!engine.serverSessions)
|
|
22125
|
+
throw new KernelError("ENOTSUP", "the installed tls engine has no server sessions");
|
|
22126
|
+
} catch (err) {
|
|
22127
|
+
wipe(cert);
|
|
22128
|
+
throw err;
|
|
22129
|
+
}
|
|
22130
|
+
const leaf = leafOf(engine, cert);
|
|
22131
|
+
let store;
|
|
22132
|
+
try {
|
|
22133
|
+
store = req.ca && req.ca.length > 0 ? storeOf(engine, req.ca) : void 0;
|
|
22134
|
+
const flags2 = flagsOf(req, store);
|
|
22135
|
+
return {
|
|
22136
|
+
engine,
|
|
22137
|
+
leaf,
|
|
22138
|
+
store,
|
|
22139
|
+
alpn: req.alpn ?? [],
|
|
22140
|
+
flags: flags2,
|
|
22141
|
+
timeoutMs: timeoutOf(req),
|
|
22142
|
+
release() {
|
|
22143
|
+
leaf.release();
|
|
22144
|
+
store?.release();
|
|
22145
|
+
}
|
|
22146
|
+
};
|
|
22147
|
+
} catch (err) {
|
|
22148
|
+
leaf.release();
|
|
22149
|
+
store?.release();
|
|
22150
|
+
throw err;
|
|
22151
|
+
}
|
|
22152
|
+
}
|
|
22153
|
+
async function listenSecure(proc, socket, req) {
|
|
22154
|
+
if (!socket.listening) throw new KernelError("EINVAL", "not a listening socket");
|
|
22155
|
+
const server = await serverOf(proc, req);
|
|
22156
|
+
socket.serving?.release();
|
|
22157
|
+
socket.serving = server;
|
|
22158
|
+
}
|
|
22159
|
+
async function serveSecure(proc, socket, req) {
|
|
22160
|
+
const shared = req.listener?.serving;
|
|
22161
|
+
if (req.listener && !shared) throw new KernelError("EINVAL", "the listener has no tls");
|
|
22162
|
+
const server = shared ?? await serverOf(proc, req);
|
|
22163
|
+
try {
|
|
22164
|
+
const { engine, leaf, alpn, store, flags: flags2 } = server;
|
|
22165
|
+
const limit = handshakeSignal(proc, server.timeoutMs);
|
|
22166
|
+
const info = await socket.secureLink(
|
|
22167
|
+
(raw2) => tlsServer(raw2, engine.serverSession(leaf, alpn, store, flags2), flags2 !== 0),
|
|
22168
|
+
limit.signal
|
|
22169
|
+
).finally(limit.done);
|
|
22170
|
+
return {
|
|
22171
|
+
path: "kernel",
|
|
22172
|
+
protocol: info.protocol,
|
|
22173
|
+
alpnProtocol: info.alpnProtocol,
|
|
22174
|
+
servername: info.servername,
|
|
22175
|
+
authorized: info.authorized,
|
|
22176
|
+
...info.authorizationError ? { authorizationError: info.authorizationError } : {},
|
|
22177
|
+
peerCertificates: info.peerCertificates.map(encode)
|
|
22178
|
+
};
|
|
22179
|
+
} finally {
|
|
22180
|
+
if (!shared) server.release();
|
|
22181
|
+
}
|
|
22182
|
+
}
|
|
22183
|
+
|
|
21912
22184
|
// src/kernel/unix-dgram.ts
|
|
21913
22185
|
var SOL_SOCKET3 = 1;
|
|
21914
22186
|
var SO_TYPE3 = 3;
|
|
@@ -22071,7 +22343,9 @@ var SOCKET_OPS = [
|
|
|
22071
22343
|
"sock-sendto",
|
|
22072
22344
|
"sock-recvfrom",
|
|
22073
22345
|
"sock-tls",
|
|
22074
|
-
"sock-tls-upgrade"
|
|
22346
|
+
"sock-tls-upgrade",
|
|
22347
|
+
"sock-tls-serve",
|
|
22348
|
+
"sock-tls-listen"
|
|
22075
22349
|
];
|
|
22076
22350
|
function socketAt(fds, fd) {
|
|
22077
22351
|
const file = anySocketAt(fds, fd);
|
|
@@ -22147,6 +22421,14 @@ async function socketSyscall(req, proc) {
|
|
|
22147
22421
|
return ok3(await secureConnect(req, proc));
|
|
22148
22422
|
case "sock-tls-upgrade":
|
|
22149
22423
|
return ok3(await upgradeSecure(proc, socketAt(fds, req.fd), req));
|
|
22424
|
+
case "sock-tls-serve": {
|
|
22425
|
+
const { from, ...rest2 } = req;
|
|
22426
|
+
const listener = from === void 0 ? {} : { listener: socketAt(fds, from) };
|
|
22427
|
+
return ok3(await serveSecure(proc, socketAt(fds, req.fd), { ...rest2, ...listener }));
|
|
22428
|
+
}
|
|
22429
|
+
case "sock-tls-listen":
|
|
22430
|
+
await listenSecure(proc, socketAt(fds, req.fd), req);
|
|
22431
|
+
return ok3({});
|
|
22150
22432
|
default:
|
|
22151
22433
|
return datagram(req, proc);
|
|
22152
22434
|
}
|
|
@@ -24578,6 +24860,7 @@ var WasmProcess = class {
|
|
|
24578
24860
|
fds: this.fds,
|
|
24579
24861
|
net: this.net,
|
|
24580
24862
|
blocking: () => this.blockingSignal(cancelled),
|
|
24863
|
+
cancelled,
|
|
24581
24864
|
resolve: (name2, family, blocking) => resolver.resolve(name2, family, blocking),
|
|
24582
24865
|
cred: () => ({ pid: this.pid, uid: this.cred.euid, gid: this.cred.egid }),
|
|
24583
24866
|
stamp: () => this.connectorStamp(),
|